]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Move rendering into st-theme-node-drawing.c
authorColin Walters <walters@verbum.org>
Mon, 8 Feb 2010 18:40:25 +0000 (13:40 -0500)
committerColin Walters <walters@verbum.org>
Wed, 24 Mar 2010 18:14:03 +0000 (14:14 -0400)
The idea behind this move is that we have a lot more control over
rendering if StWidget isn't a big pile of actors, and things are
more efficient.

https://bugzilla.gnome.org/show_bug.cgi?id=607500

src/Makefile-st.am
src/st/st-button.c
src/st/st-shadow-texture.c [deleted file]
src/st/st-shadow-texture.h [deleted file]
src/st/st-texture-frame.c [deleted file]
src/st/st-texture-frame.h [deleted file]
src/st/st-theme-node-drawing.c [new file with mode: 0644]
src/st/st-theme-node-private.h [new file with mode: 0644]
src/st/st-theme-node.c
src/st/st-theme-node.h
src/st/st-widget.c

index 706c5884051c0b0bf016611b56d5799f8bd185a7..6b06688b023429e673e950572c615b812ecf02d1 100644 (file)
@@ -83,13 +83,11 @@ st_source_h =                                       \
     st/st-scrollable.h                         \
     st/st-scroll-bar.h                         \
     st/st-scroll-view.h                                \
-    st/st-shadow-texture.h                      \
     st/st-shadow.h                              \
     st/st-subtexture.h                         \
     st/st-table.h                              \
     st/st-table-child.h                                \
     st/st-texture-cache.h                      \
-    st/st-texture-frame.h                      \
     st/st-theme.h                              \
     st/st-theme-context.h                      \
     st/st-theme-node.h                         \
@@ -109,7 +107,8 @@ BUILT_SOURCES += st.h
 st_source_private_h =                          \
     st/st-private.h                            \
     st/st-table-private.h                      \
-    st/st-theme-private.h
+    st/st-theme-private.h                      \
+    st/st-theme-node-private.h
 
 # please, keep this sorted alphabetically
 st_source_c =                                  \
@@ -130,16 +129,15 @@ st_source_c =                                     \
     st/st-scrollable.c                         \
     st/st-scroll-bar.c                         \
     st/st-scroll-view.c                                \
-    st/st-shadow-texture.c                      \
     st/st-shadow.c                              \
     st/st-subtexture.c                         \
     st/st-table.c                              \
     st/st-table-child.c                                \
     st/st-texture-cache.c                      \
-    st/st-texture-frame.c                      \
     st/st-theme.c                              \
     st/st-theme-context.c                      \
     st/st-theme-node.c                         \
+    st/st-theme-node-drawing.c         \
     st/st-tooltip.c                            \
     st/st-widget.c                             \
     $(NULL)
index 2b8d7574ebde0c86647d804557dd8c21de713cbe..7fbc101d8302bafac8b95adef10e63288804368e 100644 (file)
@@ -45,7 +45,6 @@
 #include "st-button.h"
 
 #include "st-marshal.h"
-#include "st-texture-frame.h"
 #include "st-texture-cache.h"
 #include "st-private.h"
 
diff --git a/src/st/st-shadow-texture.c b/src/st/st-shadow-texture.c
deleted file mode 100644 (file)
index 04b6b90..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-#include <math.h>
-#include <string.h>
-
-#include "st-shadow-texture.h"
-
-/**
- * SECTION: st-shadow-texture
- * @short_description: a class for creating soft shadow textures
- *
- * #StShadowTexture is a #ClutterTexture holding a soft shadow texture for
- * another #ClutterActor.
- * It is used to implement the box-shadow property in StWidget and should
- * not be used stand-alone.
- */
-
-struct _StShadowTexture {
-  ClutterTexture parent;
-
-  CoglColor color;
-  gdouble sigma;
-  gdouble blur_radius;
-};
-
-struct _StShadowTextureClass {
-  ClutterTextureClass parent_class;
-};
-
-G_DEFINE_TYPE (StShadowTexture, st_shadow_texture, CLUTTER_TYPE_TEXTURE);
-
-static gdouble *
-calculate_gaussian_kernel (gdouble sigma, guint n_values)
-{
-  gdouble *ret, sum;
-  gdouble exp_divisor;
-  gint half, i;
-
-  g_return_val_if_fail ((int) sigma > 0, NULL);
-
-  half = n_values / 2;
-
-  ret = g_malloc (n_values * sizeof (gdouble));
-  sum = 0.0;
-
-  exp_divisor = 2 * sigma * sigma;
-
-  /* n_values of 1D Gauss function */
-  for (i = 0; i < n_values; i++)
-    {
-      ret[i] = exp (-(i - half) * (i - half) / exp_divisor);
-      sum += ret[i];
-    }
-
-  /* normalize */
-  for (i = 0; i < n_values; i++)
-    ret[i] /= sum;
-
-  return ret;
-}
-
-static void
-st_shadow_texture_create_shadow (StShadowTexture *st,
-                                 ClutterActor    *actor)
-{
-  CoglHandle  texture, material;
-  guchar     *pixels_in, *pixels_out;
-  gint        width_in, height_in, rowstride_in;
-  gint        width_out, height_out, rowstride_out;
-
-  g_return_if_fail (ST_IS_SHADOW_TEXTURE (st));
-
-  /* Right now we only deal with actors of type ClutterTexture.
-     It would be nice to extend this to generic actors with some
-     clutter_texture_new_from_actor magic in the future */
-  g_return_if_fail (CLUTTER_IS_TEXTURE (actor));
-
-  texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (actor));
-  if (texture == COGL_INVALID_HANDLE)
-    return;
-
-  width_in  = cogl_texture_get_width  (texture);
-  height_in = cogl_texture_get_height (texture);
-  rowstride_in = (width_in + 3) & ~3;
-
-  pixels_in  = g_malloc0 (rowstride_in * height_in);
-
-  cogl_texture_get_data (texture, COGL_PIXEL_FORMAT_A_8,
-                         rowstride_in, pixels_in);
-
-  if ((guint) st->blur_radius == 0)
-    {
-      width_out  = width_in;
-      height_out = height_in;
-      rowstride_out = rowstride_in;
-      pixels_out = g_memdup (pixels_in, rowstride_out * height_out);
-    }
-  else
-    {
-      gdouble *kernel;
-      guchar  *line;
-      gint     n_values, half;
-      gint     x_in, y_in, x_out, y_out, i;
-
-      n_values = (gint) 5 * st->sigma;
-      half = n_values / 2;
-
-      width_out  = width_in  + 2 * half;
-      height_out = height_in + 2 * half;
-      rowstride_out = (width_out + 3) & ~3;
-
-      pixels_out = g_malloc0 (rowstride_out * height_out);
-      line       = g_malloc0 (rowstride_out);
-
-      kernel = calculate_gaussian_kernel (st->sigma, n_values);
-
-      /* vertical blur */
-      for (x_in = 0; x_in < width_in; x_in++)
-        for (y_out = 0; y_out < height_out; y_out++)
-          {
-            guchar *pixel_in, *pixel_out;
-            gint i0, i1;
-
-            y_in = y_out - half;
-
-            /* We read from the source at 'y = y_in + i - half'; clamp the
-             * full i range [0, n_values) so that y is in [0, height_in).
-             */
-            i0 = MAX (half - y_in, 0);
-            i1 = MIN (height_in + half - y_in, n_values);
-
-            pixel_in  =  pixels_in + (y_in + i0 - half) * rowstride_in + x_in;
-            pixel_out =  pixels_out + y_out * rowstride_out + (x_in + half);
-
-            for (i = i0; i < i1; i++)
-              {
-                *pixel_out += *pixel_in * kernel[i];
-                pixel_in += rowstride_in;
-              }
-          }
-
-      /* horizontal blur */
-      for (y_out = 0; y_out < height_out; y_out++)
-        {
-          memcpy (line, pixels_out + y_out * rowstride_out, rowstride_out);
-
-          for (x_out = 0; x_out < width_out; x_out++)
-            {
-              gint i0, i1;
-              guchar *pixel_out, *pixel_in;
-
-              /* We read from the source at 'x = x_out + i - half'; clamp the
-               * full i range [0, n_values) so that x is in [0, width_out).
-               */
-              i0 = MAX (half - x_out, 0);
-              i1 = MIN (width_out + half - x_out, n_values);
-
-              pixel_in  = line + x_out + i0 - half;
-              pixel_out = pixels_out + rowstride_out * y_out + x_out;
-
-              *pixel_out = 0;
-              for (i = i0; i < i1; i++)
-                {
-                  *pixel_out += *pixel_in * kernel[i];
-                  pixel_in++;
-                }
-            }
-        }
-      g_free (kernel);
-      g_free (line);
-    }
-
-  material = cogl_material_new ();
-  texture = cogl_texture_new_from_data (width_out,
-                                        height_out,
-                                        COGL_TEXTURE_NONE,
-                                        COGL_PIXEL_FORMAT_A_8,
-                                        COGL_PIXEL_FORMAT_A_8,
-                                        rowstride_out,
-                                        pixels_out);
-
-  cogl_material_set_layer_combine_constant (material, 0, &st->color);
-  cogl_material_set_layer (material, 0, texture);
-
-  /* We ignore the material color, which encodes the overall opacity of the
-   * actor, so setting an ancestor of the shadow to partially opaque won't
-   * work. The easiest way to fix this would be to override paint(). */
-
-  cogl_material_set_layer_combine (material, 0,
-                                   "RGBA = MODULATE (CONSTANT, TEXTURE[A])",
-                                   NULL);
-
-  clutter_texture_set_cogl_material (CLUTTER_TEXTURE (st), material);
-
-  cogl_handle_unref  (texture);
-  cogl_handle_unref (material);
-
-  g_free (pixels_in);
-  g_free (pixels_out);
-}
-
-
-/**
- * st_shadow_texture_adjust_allocation:
- * @shadow: a #StShadowTexture
- * @allocation: the original allocation of @shadow
- *
- * Adjust @allocation to account for size change caused by blurrimg
- */
-void
-st_shadow_texture_adjust_allocation (StShadowTexture *shadow,
-                                     ClutterActorBox *allocation)
-{
-  g_return_if_fail (ST_IS_SHADOW_TEXTURE (shadow));
-  g_return_if_fail (allocation != NULL);
-
-  allocation->x1 -= shadow->blur_radius;
-  allocation->y1 -= shadow->blur_radius;
-  allocation->x2 += shadow->blur_radius;
-  allocation->y2 += shadow->blur_radius;
-}
-
-
-/**
- * st_shadow_texture_new:
- * @actor: the original actor
- * @color: (allow-none): the shadow color
- * @blur: the shadow's blur radius
- *
- * Create a shadow texture for @actor. When %NULL is passed for @color, it
- * defaults to fully opaque black.
- *
- * Returns: a new #ClutterActor holding a shadow texture for @actor
- */
-ClutterActor *
-st_shadow_texture_new (ClutterActor *actor,
-                       ClutterColor *color,
-                       gdouble       blur)
-{
-  StShadowTexture *st = g_object_new (ST_TYPE_SHADOW_TEXTURE, NULL);
-
-  if (color)
-    {
-      cogl_color_set_from_4ub (&st->color,
-                               color->red,  color->green,
-                               color->blue, color->alpha);
-      cogl_color_premultiply (&st->color);
-    }
-
-  st->blur_radius = blur;
-  /* we use an approximation of the sigma - blur radius relationship used
-     in Firefox for doing SVG blurs; see
-     http://mxr.mozilla.org/mozilla-central/source/gfx/thebes/src/gfxBlur.cpp#280
-  */
-  st->sigma = blur / 1.9;
-
-  st_shadow_texture_create_shadow (st, actor);
-
-  return CLUTTER_ACTOR (st);
-}
-
-static void
-st_shadow_texture_init (StShadowTexture *st)
-{
-  st->sigma  = 0.0;
-  st->blur_radius = 0.0;
-
-  cogl_color_set_from_4ub (&st->color, 0x0, 0x0, 0x0, 0xff);
-}
-
-static void
-st_shadow_texture_class_init (StShadowTextureClass *klass)
-{
-}
diff --git a/src/st/st-shadow-texture.h b/src/st/st-shadow-texture.h
deleted file mode 100644 (file)
index 3f5b28a..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-#ifndef __ST_SHADOW_TEXTURE__
-#define __ST_SHADOW_TEXTURE__
-
-#include <clutter/clutter.h>
-
-G_BEGIN_DECLS
-
-typedef struct _StShadowTexture      StShadowTexture;
-typedef struct _StShadowTextureClass StShadowTextureClass;
-
-#define ST_TYPE_SHADOW_TEXTURE            (st_shadow_texture_get_type ())
-#define ST_SHADOW_TEXTURE(object)         (G_TYPE_CHECK_INSTANCE_CAST((object),ST_TYPE_SHADOW_TEXTURE, StShadowTexture))
-#define ST_IS_SHADOW_TEXTURE(object)      (G_TYPE_CHECK_INSTANCE_TYPE((object),ST_TYPE_SHADOW_TEXTURE))
-#define ST_SHADOW_TEXTURE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),    ST_TYPE_SHADOW_TEXTURE, StShadowTextureClass))
-#define ST_IS_SHADOW_TEXTURE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),    ST_TYPE_SHADOW_TEXTURE))
-#define ST_SHADOW_TEXTURE_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), ST_TYPE_SHADOW_TEXTURE, StShadowTextureClass))
-
-GType st_shadow_texture_get_type (void) G_GNUC_CONST;
-
-ClutterActor *st_shadow_texture_new (ClutterActor *actor,
-                                     ClutterColor *color,
-                                     gdouble       blur_radius);
-
-void st_shadow_texture_adjust_allocation (StShadowTexture *shadow,
-                                          ClutterActorBox *allocation);
-
-G_END_DECLS
-
-#endif /* __ST_SHADOW_TEXTURE__ */
diff --git a/src/st/st-texture-frame.c b/src/st/st-texture-frame.c
deleted file mode 100644 (file)
index 45b4bf9..0000000
+++ /dev/null
@@ -1,621 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-/*
- * st-texture-frame.h: Expandible texture actor
- *
- * Copyright 2007 OpenedHand
- * Copyright 2009 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- * Boston, MA 02111-1307, USA.
- *
- */
-
-/**
- * SECTION:st-texture-frame
- * @short_description: Stretch a texture to fit the entire allocation
- *
- * #StTextureFrame
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <cogl/cogl.h>
-
-#include "st-texture-frame.h"
-#include "st-private.h"
-
-enum
-{
-  PROP_0,
-
-  PROP_PARENT_TEXTURE,
-
-  PROP_TOP,
-  PROP_RIGHT,
-  PROP_BOTTOM,
-  PROP_LEFT
-};
-
-G_DEFINE_TYPE (StTextureFrame, st_texture_frame, CLUTTER_TYPE_ACTOR);
-
-#define ST_TEXTURE_FRAME_GET_PRIVATE(obj)     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFramePrivate))
-
-struct _StTextureFramePrivate
-{
-  ClutterTexture *parent_texture;
-
-  gfloat          top;
-  gfloat          right;
-  gfloat          bottom;
-  gfloat          left;
-};
-
-static void
-st_texture_frame_get_preferred_width (ClutterActor *self,
-                                      gfloat        for_height,
-                                      gfloat       *min_width_p,
-                                      gfloat       *natural_width_p)
-{
-  StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
-
-  if (G_UNLIKELY (priv->parent_texture == NULL))
-    {
-      if (min_width_p)
-        *min_width_p = 0;
-
-      if (natural_width_p)
-        *natural_width_p = 0;
-    }
-  else
-    {
-      ClutterActorClass *klass;
-
-      /* by directly querying the parent texture's class implementation
-       * we are going around any override mechanism the parent texture
-       * might have in place, and we ask directly for the original
-       * preferred width
-       */
-      klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
-      klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture),
-                                  for_height,
-                                  min_width_p,
-                                  natural_width_p);
-    }
-}
-
-static void
-st_texture_frame_get_preferred_height (ClutterActor *self,
-                                       gfloat        for_width,
-                                       gfloat       *min_height_p,
-                                       gfloat       *natural_height_p)
-{
-  StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
-
-  if (G_UNLIKELY (priv->parent_texture == NULL))
-    {
-      if (min_height_p)
-        *min_height_p = 0;
-
-      if (natural_height_p)
-        *natural_height_p = 0;
-    }
-  else
-    {
-      ClutterActorClass *klass;
-
-      /* by directly querying the parent texture's class implementation
-       * we are going around any override mechanism the parent texture
-       * might have in place, and we ask directly for the original
-       * preferred height
-       */
-      klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
-      klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture),
-                                   for_width,
-                                   min_height_p,
-                                   natural_height_p);
-    }
-}
-
-static void
-st_texture_frame_paint (ClutterActor *self)
-{
-  StTextureFramePrivate *priv = ST_TEXTURE_FRAME (self)->priv;
-  CoglHandle cogl_texture = COGL_INVALID_HANDLE;
-  CoglHandle cogl_material = COGL_INVALID_HANDLE;
-  ClutterActorBox box = { 0, };
-  gfloat width, height;
-  gfloat tex_width, tex_height;
-  gfloat ex, ey;
-  gfloat tx1, ty1, tx2, ty2;
-  guint8 opacity;
-
-  /* no need to paint stuff if we don't have a texture */
-  if (G_UNLIKELY (priv->parent_texture == NULL))
-    return;
-
-  /* parent texture may have been hidden, so need to make sure it gets
-   * realized
-   */
-  if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
-    clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
-
-  cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
-  if (cogl_texture == COGL_INVALID_HANDLE)
-    return;
-  cogl_material = clutter_texture_get_cogl_material (priv->parent_texture);
-  if (cogl_material == COGL_INVALID_HANDLE)
-    return;
-
-  tex_width  = cogl_texture_get_width (cogl_texture);
-  tex_height = cogl_texture_get_height (cogl_texture);
-
-  clutter_actor_get_allocation_box (self, &box);
-  width = box.x2 - box.x1;
-  height = box.y2 - box.y1;
-
-  tx1 = priv->left / tex_width;
-  tx2 = (tex_width - priv->right) / tex_width;
-  ty1 = priv->top / tex_height;
-  ty2 = (tex_height - priv->bottom) / tex_height;
-
-  ex = width - priv->right;
-  if (ex < 0)
-    ex = priv->right;           /* FIXME ? */
-
-  ey = height - priv->bottom;
-  if (ey < 0)
-    ey = priv->bottom;          /* FIXME ? */
-
-  opacity = clutter_actor_get_paint_opacity (self);
-
-  /* Paint using the parent texture's material. It should already have
-     the cogl texture set as the first layer */
-  /* NB: for correct blending we need set a preumultiplied color here: */
-  cogl_material_set_color4ub (cogl_material,
-                              opacity, opacity, opacity, opacity);
-  cogl_set_source (cogl_material);
-
-  {
-    GLfloat rectangles[] =
-    {
-      /* top left corner */
-      0, 0, priv->left, priv->top,
-      0.0, 0.0,
-      tx1, ty1,
-
-      /* top middle */
-      priv->left, 0, ex, priv->top,
-      tx1, 0.0,
-      tx2, ty1,
-
-      /* top right */
-      ex, 0, width, priv->top,
-      tx2, 0.0,
-      1.0, ty1,
-
-      /* mid left */
-      0, priv->top, priv->left, ey,
-      0.0, ty1,
-      tx1, ty2,
-
-      /* center */
-      priv->left, priv->top, ex, ey,
-      tx1, ty1,
-      tx2, ty2,
-
-      /* mid right */
-      ex, priv->top, width, ey,
-      tx2, ty1,
-      1.0, ty2,
-
-      /* bottom left */
-      0, ey, priv->left, height,
-      0.0, ty2,
-      tx1, 1.0,
-
-      /* bottom center */
-      priv->left, ey, ex, height,
-      tx1, ty2,
-      tx2, 1.0,
-
-      /* bottom right */
-      ex, ey, width, height,
-      tx2, ty2,
-      1.0, 1.0
-    };
-
-    cogl_rectangles_with_texture_coords (rectangles, 9);
-  }
-}
-
-static inline void
-st_texture_frame_set_frame_internal (StTextureFrame *frame,
-                                     gfloat          top,
-                                     gfloat          right,
-                                     gfloat          bottom,
-                                     gfloat          left)
-{
-  StTextureFramePrivate *priv = frame->priv;
-  GObject *gobject = G_OBJECT (frame);
-  gboolean changed = FALSE;
-
-  g_object_freeze_notify (gobject);
-
-  if (priv->top != top)
-    {
-      priv->top = top;
-      g_object_notify (gobject, "top");
-      changed = TRUE;
-    }
-
-  if (priv->right != right)
-    {
-      priv->right = right;
-      g_object_notify (gobject, "right");
-      changed = TRUE;
-    }
-
-  if (priv->bottom != bottom)
-    {
-      priv->bottom = bottom;
-      g_object_notify (gobject, "bottom");
-      changed = TRUE;
-    }
-
-  if (priv->left != left)
-    {
-      priv->left = left;
-      g_object_notify (gobject, "left");
-      changed = TRUE;
-    }
-
-  if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
-    clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
-
-  g_object_thaw_notify (gobject);
-}
-
-static void
-st_texture_frame_set_property (GObject      *gobject,
-                               guint         prop_id,
-                               const GValue *value,
-                               GParamSpec   *pspec)
-{
-  StTextureFrame *frame = ST_TEXTURE_FRAME (gobject);
-  StTextureFramePrivate *priv = frame->priv;
-
-  switch (prop_id)
-    {
-    case PROP_PARENT_TEXTURE:
-      st_texture_frame_set_parent_texture (frame,
-                                           g_value_get_object (value));
-      break;
-
-    case PROP_TOP:
-      st_texture_frame_set_frame_internal (frame,
-                                           g_value_get_float (value),
-                                           priv->right,
-                                           priv->bottom,
-                                           priv->left);
-      break;
-
-    case PROP_RIGHT:
-      st_texture_frame_set_frame_internal (frame,
-                                           priv->top,
-                                           g_value_get_float (value),
-                                           priv->bottom,
-                                           priv->left);
-      break;
-
-    case PROP_BOTTOM:
-      st_texture_frame_set_frame_internal (frame,
-                                           priv->top,
-                                           priv->right,
-                                           g_value_get_float (value),
-                                           priv->left);
-      break;
-
-    case PROP_LEFT:
-      st_texture_frame_set_frame_internal (frame,
-                                           priv->top,
-                                           priv->right,
-                                           priv->bottom,
-                                           g_value_get_float (value));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-st_texture_frame_get_property (GObject    *gobject,
-                               guint       prop_id,
-                               GValue     *value,
-                               GParamSpec *pspec)
-{
-  StTextureFramePrivate *priv = ST_TEXTURE_FRAME (gobject)->priv;
-
-  switch (prop_id)
-    {
-    case PROP_PARENT_TEXTURE:
-      g_value_set_object (value, priv->parent_texture);
-      break;
-
-    case PROP_LEFT:
-      g_value_set_float (value, priv->left);
-      break;
-
-    case PROP_TOP:
-      g_value_set_float (value, priv->top);
-      break;
-
-    case PROP_RIGHT:
-      g_value_set_float (value, priv->right);
-      break;
-
-    case PROP_BOTTOM:
-      g_value_set_float (value, priv->bottom);
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
-      break;
-    }
-}
-
-static void
-st_texture_frame_dispose (GObject *gobject)
-{
-  StTextureFramePrivate *priv = ST_TEXTURE_FRAME (gobject)->priv;
-
-  if (priv->parent_texture)
-    {
-      g_object_unref (priv->parent_texture);
-      priv->parent_texture = NULL;
-    }
-
-  G_OBJECT_CLASS (st_texture_frame_parent_class)->dispose (gobject);
-}
-
-static void
-st_texture_frame_class_init (StTextureFrameClass *klass)
-{
-  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
-  GParamSpec *pspec;
-
-  g_type_class_add_private (gobject_class, sizeof (StTextureFramePrivate));
-
-  actor_class->get_preferred_width =
-    st_texture_frame_get_preferred_width;
-  actor_class->get_preferred_height =
-    st_texture_frame_get_preferred_height;
-  actor_class->paint = st_texture_frame_paint;
-
-  gobject_class->set_property = st_texture_frame_set_property;
-  gobject_class->get_property = st_texture_frame_get_property;
-  gobject_class->dispose = st_texture_frame_dispose;
-
-  pspec = g_param_spec_object ("parent-texture",
-                               "Parent Texture",
-                               "The parent ClutterTexture",
-                               CLUTTER_TYPE_TEXTURE,
-                               ST_PARAM_READWRITE |
-                               G_PARAM_CONSTRUCT);
-  g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
-
-  pspec = g_param_spec_float ("left",
-                              "Left",
-                              "Left offset",
-                              0, G_MAXFLOAT,
-                              0,
-                              ST_PARAM_READWRITE);
-  g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
-
-  pspec = g_param_spec_float ("top",
-                              "Top",
-                              "Top offset",
-                              0, G_MAXFLOAT,
-                              0,
-                              ST_PARAM_READWRITE);
-  g_object_class_install_property (gobject_class, PROP_TOP, pspec);
-
-  pspec = g_param_spec_float ("bottom",
-                              "Bottom",
-                              "Bottom offset",
-                              0, G_MAXFLOAT,
-                              0,
-                              ST_PARAM_READWRITE);
-  g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec);
-
-  pspec = g_param_spec_float ("right",
-                              "Right",
-                              "Right offset",
-                              0, G_MAXFLOAT,
-                              0,
-                              ST_PARAM_READWRITE);
-  g_object_class_install_property (gobject_class, PROP_RIGHT, pspec);
-}
-
-static void
-st_texture_frame_init (StTextureFrame *self)
-{
-  StTextureFramePrivate *priv;
-
-  self->priv = priv = ST_TEXTURE_FRAME_GET_PRIVATE (self);
-}
-
-/**
- * st_texture_frame_new:
- * @texture: a #ClutterTexture or %NULL
- * @left: left margin preserving its content
- * @top: top margin preserving its content
- * @right: right margin preserving its content
- * @bottom: bottom margin preserving its content
- *
- * A #StTextureFrame is a specialized texture that efficiently clones
- * an area of the given @texture while keeping preserving portions of the
- * same texture.
- *
- * A #StTextureFrame can be used to make a rectangular texture fit a
- * given size without stretching its borders.
- *
- * Return value: the newly created #StTextureFrame
- */
-ClutterActor*
-st_texture_frame_new (ClutterTexture *texture,
-                      gfloat          top,
-                      gfloat          right,
-                      gfloat          bottom,
-                      gfloat          left)
-{
-  g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
-
-  return g_object_new (ST_TYPE_TEXTURE_FRAME,
-                       "parent-texture", texture,
-                       "top", top,
-                       "right", right,
-                       "bottom", bottom,
-                       "left", left,
-                       NULL);
-}
-
-/**
- * st_texture_frame_get_parent_texture:
- * @frame: A #StTextureFrame
- *
- * Return the texture used by the #StTextureFrame
- *
- * Returns: (transfer none): a #ClutterTexture owned by the #StTextureFrame
- */
-ClutterTexture *
-st_texture_frame_get_parent_texture (StTextureFrame *frame)
-{
-  g_return_val_if_fail (ST_IS_TEXTURE_FRAME (frame), NULL);
-
-  return frame->priv->parent_texture;
-}
-
-/**
- * st_texture_frame_set_parent_texture:
- * @frame: A #StTextureFrame
- * @texture: A #ClutterTexture
- *
- * Set the #ClutterTexture used by this #StTextureFrame
- *
- */
-void
-st_texture_frame_set_parent_texture (StTextureFrame *frame,
-                                     ClutterTexture *texture)
-{
-  StTextureFramePrivate *priv;
-  gboolean was_visible;
-
-  g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
-  g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
-
-  priv = frame->priv;
-
-  was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
-
-  if (priv->parent_texture == texture)
-    return;
-
-  if (priv->parent_texture)
-    {
-      g_object_unref (priv->parent_texture);
-      priv->parent_texture = NULL;
-
-      if (was_visible)
-        clutter_actor_hide (CLUTTER_ACTOR (frame));
-    }
-
-  if (texture)
-    {
-      priv->parent_texture = g_object_ref_sink (texture);
-
-      if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
-        clutter_actor_show (CLUTTER_ACTOR (frame));
-    }
-
-  clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
-
-  g_object_notify (G_OBJECT (frame), "parent-texture");
-}
-
-/**
- * st_texture_frame_set_frame:
- * @frame: A #StTextureFrame
- * @top: width of the top slice
- * @right: width of the right slice
- * @bottom: width of the bottom slice
- * @left: width of the left slice
- *
- * Set the slice lines of the specified frame. The slices are calculated as
- * widths from the edge of the frame.
- *
- */
-void
-st_texture_frame_set_frame (StTextureFrame *frame,
-                            gfloat          top,
-                            gfloat          right,
-                            gfloat          bottom,
-                            gfloat          left)
-{
-  g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
-
-  st_texture_frame_set_frame_internal (frame, top, right, bottom, left);
-}
-
-/**
- * st_texture_frame_get_frame:
- * @frame: A #StTextureFrame
- * @top: width of the top slice
- * @right: width of the right slice
- * @bottom: width of the bottom slice
- * @left: width of the left slice
- *
- * Retrieve the current slice lines from the specified frame.
- *
- */
-void
-st_texture_frame_get_frame (StTextureFrame *frame,
-                            gfloat         *top,
-                            gfloat         *right,
-                            gfloat         *bottom,
-                            gfloat         *left)
-{
-  StTextureFramePrivate *priv;
-
-  g_return_if_fail (ST_IS_TEXTURE_FRAME (frame));
-
-  priv = frame->priv;
-
-  if (top)
-    *top = priv->top;
-
-  if (right)
-    *right = priv->right;
-
-  if (bottom)
-    *bottom = priv->bottom;
-
-  if (left)
-    *left = priv->left;
-}
diff --git a/src/st/st-texture-frame.h b/src/st/st-texture-frame.h
deleted file mode 100644 (file)
index a4e4463..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
-/*
- * st-texture-frame.h: Expandible texture actor
- *
- * Copyright 2007, 2008 OpenedHand Ltd
- * Copyright 2009 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU Lesser General Public License,
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
- * more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- * Boston, MA 02111-1307, USA.
- *
- */
-
-#if !defined(ST_H_INSIDE) && !defined(ST_COMPILATION)
-#error "Only <st/st.h> can be included directly.h"
-#endif
-
-#ifndef __ST_TEXTURE_FRAME_H__
-#define __ST_TEXTURE_FRAME_H__
-
-#include <clutter/clutter.h>
-
-G_BEGIN_DECLS
-
-#define ST_TYPE_TEXTURE_FRAME                 (st_texture_frame_get_type ())
-#define ST_TEXTURE_FRAME(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFrame))
-#define ST_TEXTURE_FRAME_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), ST_TYPE_TEXTURE_FRAME, StTextureFrameClass))
-#define ST_IS_TEXTURE_FRAME(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ST_TYPE_TEXTURE_FRAME))
-#define ST_IS_TEXTURE_FRAME_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), ST_TYPE_TEXTURE_FRAME))
-#define ST_TEXTURE_FRAME_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), ST_TYPE_TEXTURE_FRAME, StTextureFrameClass))
-
-typedef struct _StTextureFrame                StTextureFrame;
-typedef struct _StTextureFramePrivate         StTextureFramePrivate;
-typedef struct _StTextureFrameClass           StTextureFrameClass;
-
-/**
- * StTextureFrame:
- *
- * The contents of this structure are private and should only be accessed
- * through the public API.
- */
-struct _StTextureFrame
-{
-  /*< private >*/
-  ClutterActor parent_instance;
-  
-  StTextureFramePrivate    *priv;
-};
-
-struct _StTextureFrameClass 
-{
-  ClutterActorClass parent_class;
-
-  /* padding for future expansion */
-  void (*_clutter_box_1) (void);
-  void (*_clutter_box_2) (void);
-  void (*_clutter_box_3) (void);
-  void (*_clutter_box_4) (void);
-}; 
-
-GType st_texture_frame_get_type (void) G_GNUC_CONST;
-
-ClutterActor *  st_texture_frame_new                (ClutterTexture *texture,
-                                                     gfloat          top,
-                                                     gfloat          right,
-                                                     gfloat          bottom,
-                                                     gfloat          left);
-void            st_texture_frame_set_parent_texture (StTextureFrame *frame,
-                                                     ClutterTexture *texture);
-ClutterTexture *st_texture_frame_get_parent_texture (StTextureFrame *frame);
-void            st_texture_frame_set_frame          (StTextureFrame *frame,
-                                                     gfloat          top,
-                                                     gfloat          right,
-                                                     gfloat          bottom,
-                                                     gfloat          left);
-void            st_texture_frame_get_frame          (StTextureFrame *frame,
-                                                     gfloat         *top,
-                                                     gfloat         *right,
-                                                     gfloat         *bottom,
-                                                     gfloat         *left);
-
-G_END_DECLS
-
-#endif /* __ST_TEXTURE_FRAME_H__ */
diff --git a/src/st/st-theme-node-drawing.c b/src/st/st-theme-node-drawing.c
new file mode 100644 (file)
index 0000000..374a55c
--- /dev/null
@@ -0,0 +1,1097 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* Drawing for StWidget.
+
+   Copyright (C) 2009,2010 Red Hat, Inc.
+
+   Contains code derived from:
+   rectangle.c: Rounded rectangle.
+   Copyright (C) 2008 litl, LLC.
+   st-shadow-texture.c: a class for creating soft shadow texture
+   Copyright (C) 2009 Florian Müllner <fmuellner@src.gnome.org>
+   st-texture-frame.h: Expandible texture actor
+   Copyright 2007 OpenedHand
+   Copyright 2009 Intel Corporation.
+
+   The St is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The St is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the St; see the file COPYING.LIB.
+   If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+   Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "st-theme-private.h"
+#include "st-theme-context.h"
+#include "st-texture-cache.h"
+#include "st-theme-node-private.h"
+
+/*****
+ * Shadows
+ *****/
+
+static gdouble *
+calculate_gaussian_kernel (gdouble   sigma,
+                           guint     n_values)
+{
+  gdouble *ret, sum;
+  gdouble exp_divisor;
+  gint half, i;
+
+  g_return_val_if_fail ((int) sigma > 0, NULL);
+
+  half = n_values / 2;
+
+  ret = g_malloc (n_values * sizeof (gdouble));
+  sum = 0.0;
+
+  exp_divisor = 2 * sigma * sigma;
+
+  /* n_values of 1D Gauss function */
+  for (i = 0; i < n_values; i++)
+    {
+      ret[i] = exp (-(i - half) * (i - half) / exp_divisor);
+      sum += ret[i];
+    }
+
+  /* normalize */
+  for (i = 0; i < n_values; i++)
+    ret[i] /= sum;
+
+  return ret;
+}
+
+static CoglHandle
+create_shadow_material (StThemeNode  *node,
+                        CoglHandle    src_texture)
+{
+  CoglColor   color;
+  CoglHandle  material;
+  CoglHandle  texture;
+  StShadow   *shadow_spec;
+  guchar     *pixels_in, *pixels_out;
+  gint        width_in, height_in, rowstride_in;
+  gint        width_out, height_out, rowstride_out;
+  float       sigma;
+
+  shadow_spec = st_theme_node_get_shadow (node);
+  if (!shadow_spec)
+    return COGL_INVALID_HANDLE;
+
+  /* we use an approximation of the sigma - blur radius relationship used
+     in Firefox for doing SVG blurs; see
+     http://mxr.mozilla.org/mozilla-central/source/gfx/thebes/src/gfxBlur.cpp#280
+  */
+  sigma = shadow_spec->blur / 1.9;
+
+  width_in  = cogl_texture_get_width  (src_texture);
+  height_in = cogl_texture_get_height (src_texture);
+  rowstride_in = (width_in + 3) & ~3;
+
+  pixels_in  = g_malloc0 (rowstride_in * height_in);
+
+  cogl_texture_get_data (src_texture, COGL_PIXEL_FORMAT_A_8,
+                         rowstride_in, pixels_in);
+
+  if ((guint) shadow_spec->blur == 0)
+    {
+      width_out  = width_in;
+      height_out = height_in;
+      rowstride_out = rowstride_in;
+      pixels_out = g_memdup (pixels_in, rowstride_out * height_out);
+    }
+  else
+    {
+      gdouble *kernel;
+      guchar  *line;
+      gint     n_values, half;
+      gint     x_in, y_in, x_out, y_out, i;
+
+      n_values = (gint) 5 * sigma;
+      half = n_values / 2;
+
+      width_out  = width_in  + 2 * half;
+      height_out = height_in + 2 * half;
+      rowstride_out = (width_out + 3) & ~3;
+
+      pixels_out = g_malloc0 (rowstride_out * height_out);
+      line       = g_malloc0 (rowstride_out);
+
+      kernel = calculate_gaussian_kernel (sigma, n_values);
+
+      /* vertical blur */
+      for (x_in = 0; x_in < width_in; x_in++)
+        for (y_out = 0; y_out < height_out; y_out++)
+          {
+            guchar *pixel_in, *pixel_out;
+            gint i0, i1;
+
+            y_in = y_out - half;
+
+            /* We read from the source at 'y = y_in + i - half'; clamp the
+             * full i range [0, n_values) so that y is in [0, height_in).
+             */
+            i0 = MAX (half - y_in, 0);
+            i1 = MIN (height_in + half - y_in, n_values);
+
+            pixel_in  =  pixels_in + (y_in + i0 - half) * rowstride_in + x_in;
+            pixel_out =  pixels_out + y_out * rowstride_out + (x_in + half);
+
+            for (i = i0; i < i1; i++)
+              {
+                *pixel_out += *pixel_in * kernel[i];
+                pixel_in += rowstride_in;
+              }
+          }
+
+      /* horizontal blur */
+      for (y_out = 0; y_out < height_out; y_out++)
+        {
+          memcpy (line, pixels_out + y_out * rowstride_out, rowstride_out);
+
+          for (x_out = 0; x_out < width_out; x_out++)
+            {
+              gint i0, i1;
+              guchar *pixel_out, *pixel_in;
+
+              /* We read from the source at 'x = x_out + i - half'; clamp the
+               * full i range [0, n_values) so that x is in [0, width_out).
+               */
+              i0 = MAX (half - x_out, 0);
+              i1 = MIN (width_out + half - x_out, n_values);
+
+              pixel_in  = line + x_out + i0 - half;
+              pixel_out = pixels_out + rowstride_out * y_out + x_out;
+
+              *pixel_out = 0;
+              for (i = i0; i < i1; i++)
+                {
+                  *pixel_out += *pixel_in * kernel[i];
+                  pixel_in++;
+                }
+            }
+        }
+      g_free (kernel);
+      g_free (line);
+    }
+
+  texture = cogl_texture_new_from_data (width_out,
+                                        height_out,
+                                        COGL_TEXTURE_NONE,
+                                        COGL_PIXEL_FORMAT_A_8,
+                                        COGL_PIXEL_FORMAT_A_8,
+                                        rowstride_out,
+                                        pixels_out);
+
+  g_free (pixels_in);
+  g_free (pixels_out);
+
+  material = cogl_material_new ();
+
+  cogl_color_set_from_4ub (&color,
+                           shadow_spec->color.red, shadow_spec->color.green,
+                           shadow_spec->color.blue, shadow_spec->color.alpha);
+  cogl_color_premultiply (&color);
+
+  cogl_material_set_layer_combine_constant (material, 0, &color);
+  cogl_material_set_layer (material, 0, texture);
+
+  /* We ignore the material color, which encodes the overall opacity of the
+   * actor, so setting an ancestor of the shadow to partially opaque won't
+   * work. The easiest way to fix this would be to not set the combine or constant color
+   * here, then in paint set the material color to the shadow_spec_color *
+   * paint_opacity.*/
+  cogl_material_set_layer_combine (material, 0,
+                                   "RGBA = MODULATE (CONSTANT, TEXTURE[A])",
+                                   NULL);
+
+
+  cogl_handle_unref (texture);
+
+  return material;
+}
+
+
+/****
+ * Rounded corners
+ ****/
+
+typedef struct {
+  ClutterColor   color;
+  ClutterColor   border_color_1;
+  ClutterColor   border_color_2;
+  guint          radius;
+  guint          border_width_1;
+  guint          border_width_2;
+} StCornerSpec;
+
+static CoglHandle
+create_corner_texture (StCornerSpec *corner)
+{
+  CoglHandle texture;
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  guint rowstride;
+  guint8 *data;
+  guint size;
+  guint max_border_width;
+
+  max_border_width = MAX(corner->border_width_2, corner->border_width_1);
+  size = 2 * MAX(max_border_width, corner->radius);
+  rowstride = size * 4;
+  data = g_new0 (guint8, size * rowstride);
+
+  surface = cairo_image_surface_create_for_data (data,
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 size, size,
+                                                 rowstride);
+  cr = cairo_create (surface);
+  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+  cairo_scale (cr, size, size);
+
+  /* TODO support nonuniform border widths */
+
+  if (corner->border_width_1 < corner->radius)
+    {
+      double internal_radius = 0.5 * (1.0 - (double) corner->border_width_1 / corner->radius);
+
+      if (corner->border_width_1 != 0)
+        {
+          cairo_set_source_rgba (cr,
+                                 corner->border_color_1.red / 255.,
+                                 corner->border_color_1.green / 255.,
+                                 corner->border_color_1.blue / 255.,
+                                 corner->border_color_1.alpha / 255.);
+
+          cairo_arc (cr, 0.5, 0.5, 0.5, 0, 2 * M_PI);
+          cairo_fill (cr);
+        }
+
+      cairo_set_source_rgba (cr,
+                             corner->color.red / 255.,
+                             corner->color.green / 255.,
+                             corner->color.blue / 255.,
+                             corner->color.alpha / 255.);
+      cairo_arc (cr, 0.5, 0.5, internal_radius, 0, 2 * M_PI);
+      cairo_fill (cr);
+    }
+  else
+    {
+      double radius;
+
+      radius = (gdouble)corner->radius / corner->border_width_1;
+
+      cairo_set_source_rgba (cr,
+                             corner->border_color_1.red / 255.,
+                             corner->border_color_1.green / 255.,
+                             corner->border_color_1.blue / 255.,
+                             corner->border_color_1.alpha / 255.);
+
+      cairo_arc (cr, radius, radius, radius, M_PI, 3 * M_PI / 2);
+      cairo_line_to (cr, 1.0 - radius, 0.0);
+      cairo_arc (cr, 1.0 - radius, radius, radius, 3 * M_PI / 2, 2 * M_PI);
+      cairo_line_to (cr, 1.0, 1.0 - radius);
+      cairo_arc (cr, 1.0 - radius, 1.0 - radius, radius, 0, M_PI / 2);
+      cairo_line_to (cr, radius, 1.0);
+      cairo_arc (cr, radius, 1.0 - radius, radius, M_PI / 2, M_PI);
+      cairo_fill (cr);
+    }
+  cairo_destroy (cr);
+
+  cairo_surface_destroy (surface);
+
+  texture = cogl_texture_new_from_data (size, size,
+                                        COGL_TEXTURE_NONE,
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                                        COGL_PIXEL_FORMAT_BGRA_8888_PRE,
+#else
+                                        COGL_PIXEL_FORMAT_ARGB_8888_PRE,
+#endif
+                                        COGL_PIXEL_FORMAT_ANY,
+                                        rowstride,
+                                        data);
+  g_free (data);
+  g_assert (texture != COGL_INVALID_HANDLE);
+
+  return texture;
+}
+
+static char *
+corner_to_string (StCornerSpec *corner)
+{
+  return g_strdup_printf ("st-theme-node-corner:%02x%02x%02x%02x,%02x%02x%02x%02x,%02x%02x%02x%02x,%u,%u,%u",
+                          corner->color.red, corner->color.blue, corner->color.green, corner->color.alpha,
+                          corner->border_color_1.red, corner->border_color_1.green, corner->border_color_1.blue, corner->border_color_1.alpha,
+                          corner->border_color_2.red, corner->border_color_2.green, corner->border_color_2.blue, corner->border_color_2.alpha,
+                          corner->radius,
+                          corner->border_width_1,
+                          corner->border_width_2);
+}
+
+typedef struct {
+  StThemeNode *node;
+  StCornerSpec *corner;
+} LoadCornerData;
+
+static CoglHandle
+load_corner (StTextureCache  *cache,
+             const char      *key,
+             void            *datap,
+             GError         **error)
+{
+  LoadCornerData *data = datap;
+
+  return create_corner_texture (data->corner);
+}
+
+/* To match the CSS specification, we want the border to look like it was
+ * drawn over the background. But actually drawing the border over the
+ * background will produce slightly bad antialiasing at the edges, so
+ * compute the effective border color instead.
+ */
+#define NORM(x) (t = (x) + 127, (t + (t >> 8)) >> 8)
+#define MULT(c,a) NORM(c*a)
+
+static void
+premultiply (ClutterColor *color)
+{
+  guint t;
+  color->red = MULT (color->red, color->alpha);
+  color->green = MULT (color->green, color->alpha);
+  color->blue = MULT (color->blue, color->alpha);
+}
+
+static void
+unpremultiply (ClutterColor *color)
+{
+  if (color->alpha != 0)
+    {
+      color->red = (color->red * 255 + 127) / color->alpha;
+      color->green = (color->green * 255 + 127) / color->alpha;
+      color->blue = (color->blue * 255 + 127) / color->alpha;
+    }
+}
+
+static void
+over (const ClutterColor *source,
+      const ClutterColor *destination,
+      ClutterColor       *result)
+{
+  guint t;
+  ClutterColor src = *source;
+  ClutterColor dst = *destination;
+
+  premultiply (&src);
+  premultiply (&dst);
+
+  result->alpha = src.alpha + NORM ((255 - src.alpha) * dst.alpha);
+  result->red   = src.red +   NORM ((255 - src.alpha) * dst.red);
+  result->green = src.green + NORM ((255 - src.alpha) * dst.green);
+  result->blue  = src.blue +  NORM ((255 - src.alpha) * dst.blue);
+
+  unpremultiply (result);
+}
+
+static CoglHandle
+st_theme_node_lookup_corner (StThemeNode    *node,
+                             StCorner        corner_id)
+{
+  CoglHandle texture;
+  char *key;
+  StTextureCache *cache;
+  StCornerSpec corner;
+  LoadCornerData data;
+
+  if (node->border_radius[corner_id] == 0)
+    return COGL_INVALID_HANDLE;
+
+  cache = st_texture_cache_get_default ();
+
+  corner.radius = node->border_radius[corner_id];
+  corner.color = node->background_color;
+
+  switch (corner_id)
+    {
+      case ST_CORNER_TOPLEFT:
+        corner.border_width_1 = node->border_width[ST_SIDE_TOP];
+        corner.border_width_2 = node->border_width[ST_SIDE_LEFT];
+        over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
+        over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
+        break;
+      case ST_CORNER_TOPRIGHT:
+        corner.border_width_1 = node->border_width[ST_SIDE_TOP];
+        corner.border_width_2 = node->border_width[ST_SIDE_RIGHT];
+        over (&node->border_color[ST_SIDE_TOP], &corner.color, &corner.border_color_1);
+        over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
+        break;
+      case ST_CORNER_BOTTOMRIGHT:
+        corner.border_width_1 = node->border_width[ST_SIDE_BOTTOM];
+        corner.border_width_2 = node->border_width[ST_SIDE_RIGHT];
+        over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
+        over (&node->border_color[ST_SIDE_RIGHT], &corner.color, &corner.border_color_2);
+        break;
+      case ST_CORNER_BOTTOMLEFT:
+        corner.border_width_1 = node->border_width[ST_SIDE_BOTTOM];
+        corner.border_width_2 = node->border_width[ST_SIDE_LEFT];
+        over (&node->border_color[ST_SIDE_BOTTOM], &corner.color, &corner.border_color_1);
+        over (&node->border_color[ST_SIDE_LEFT], &corner.color, &corner.border_color_2);
+        break;
+    }
+
+  key = corner_to_string (&corner);
+
+  data.node = node;
+  data.corner = &corner;
+  texture = st_texture_cache_load (cache, key, ST_TEXTURE_CACHE_POLICY_NONE, load_corner, &data, NULL);
+
+  g_free (key);
+
+  return texture;
+}
+
+static void
+get_background_position (StThemeNode             *self,
+                         const ClutterActorBox   *allocation,
+                         ClutterActorBox         *result)
+{
+  gfloat w, h;
+
+  result->x1 = result->y1 = 0;
+  result->x2 = allocation->x2 - allocation->x1;
+  result->y2 = allocation->y2 - allocation->y1;
+
+  w = cogl_texture_get_width (self->background_texture);
+  h = cogl_texture_get_height (self->background_texture);
+
+  /* scale the background into the allocated bounds */
+  if (w > result->x2 || h > result->y2)
+    {
+      gint new_h, new_w, offset;
+      gint box_w, box_h;
+
+      box_w = (int) result->x2;
+      box_h = (int) result->y2;
+
+      /* scale to fit */
+      new_h = (int)((h / w) * ((gfloat) box_w));
+      new_w = (int)((w / h) * ((gfloat) box_h));
+
+      if (new_h > box_h)
+        {
+          /* center for new width */
+          offset = ((box_w) - new_w) * 0.5;
+          result->x1 = offset;
+          result->x2 = offset + new_w;
+
+          result->y2 = box_h;
+        }
+      else
+        {
+          /* center for new height */
+          offset = ((box_h) - new_h) * 0.5;
+          result->y1 = offset;
+          result->y2 = offset + new_h;
+
+          result->x2 = box_w;
+        }
+    }
+  else
+    {
+      /* center the background on the widget */
+      result->x1 = (int)(((allocation->x2 - allocation->x1) / 2) - (w / 2));
+      result->y1 = (int)(((allocation->y2 - allocation->y1) / 2) - (h / 2));
+      result->x2 = result->x1 + w;
+      result->y2 = result->y1 + h;
+    }
+}
+
+/* Use of this function marks code which doesn't support
+ * non-uniform widths and/or colors.
+ */
+static gboolean
+get_arbitrary_border (StThemeNode   *node,
+                      int           *width,
+                      ClutterColor  *color)
+{
+  int w;
+
+  w = st_theme_node_get_border_width (node, ST_SIDE_TOP);
+  if (w > 0)
+    {
+      if (width)
+        *width = w;
+      if (color)
+        st_theme_node_get_border_color (node, ST_SIDE_TOP, color);
+      return TRUE;
+    }
+
+  if (width)
+    *width = 0;
+  return FALSE;
+}
+
+static CoglHandle
+st_theme_node_render_gradient (StThemeNode *node)
+{
+  CoglHandle texture;
+  int radius[4], i;
+  cairo_t *cr;
+  cairo_surface_t *surface;
+  cairo_pattern_t *pattern;
+  gboolean round_border = FALSE;
+  ClutterColor border_color;
+  int border_width;
+  guint rowstride;
+  guchar *data;
+
+  rowstride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, node->alloc_width);
+  data = g_new0 (guchar, node->alloc_height * rowstride);
+  surface = cairo_image_surface_create_for_data (data,
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 node->alloc_width,
+                                                 node->alloc_height,
+                                                 rowstride);
+  cr = cairo_create (surface);
+
+  /* TODO - support non-uniform border colors and widths */
+  get_arbitrary_border (node, &border_width, &border_color);
+
+  for (i = 0; i < 4; i++)
+    {
+      radius[i] = st_theme_node_get_border_radius (node, i);
+      if (radius[i] > 0)
+        round_border = TRUE;
+    }
+
+  if (node->background_gradient_type == ST_GRADIENT_VERTICAL)
+    pattern = cairo_pattern_create_linear (0, 0, 0, node->alloc_height);
+  else if (node->background_gradient_type == ST_GRADIENT_HORIZONTAL)
+    pattern = cairo_pattern_create_linear (0, 0, node->alloc_width, 0);
+  else
+    {
+      gdouble cx, cy;
+
+      cx = node->alloc_width / 2.;
+      cy = node->alloc_height / 2.;
+      pattern = cairo_pattern_create_radial (cx, cy, 0, cx, cy, MIN (cx, cy));
+    }
+
+  cairo_pattern_add_color_stop_rgba (pattern, 0,
+                                     node->background_color.red / 255.,
+                                     node->background_color.green / 255.,
+                                     node->background_color.blue / 255.,
+                                     node->background_color.alpha / 255.);
+  cairo_pattern_add_color_stop_rgba (pattern, 1,
+                                     node->background_gradient_end.red / 255.,
+                                     node->background_gradient_end.green / 255.,
+                                     node->background_gradient_end.blue / 255.,
+                                     node->background_gradient_end.alpha / 255.);
+
+  if (round_border)
+    {
+      if (radius[ST_CORNER_TOPLEFT] > 0)
+        cairo_arc (cr,
+                   radius[ST_CORNER_TOPLEFT],
+                   radius[ST_CORNER_TOPLEFT],
+                   radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2);
+      else
+        cairo_move_to (cr, 0, 0);
+      cairo_line_to (cr, node->alloc_width - radius[ST_CORNER_TOPRIGHT], 0);
+      if (radius[ST_CORNER_TOPRIGHT] > 0)
+        cairo_arc (cr,
+                   node->alloc_width - radius[ST_CORNER_TOPRIGHT],
+                   radius[ST_CORNER_TOPRIGHT],
+                   radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
+      cairo_line_to (cr, node->alloc_width, node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT]);
+      if (radius[ST_CORNER_BOTTOMRIGHT])
+        cairo_arc (cr,
+                   node->alloc_width - radius[ST_CORNER_BOTTOMRIGHT],
+                   node->alloc_height - radius[ST_CORNER_BOTTOMRIGHT],
+                   radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
+      cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], node->alloc_height);
+      if (radius[ST_CORNER_BOTTOMLEFT])
+        cairo_arc (cr,
+                   radius[ST_CORNER_BOTTOMLEFT],
+                   node->alloc_height - radius[ST_CORNER_BOTTOMLEFT],
+                   radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI);
+      cairo_close_path (cr);
+    }
+  else
+    cairo_rectangle (cr, 0, 0, node->alloc_width, node->alloc_height);
+
+  if (border_width > 0)
+    {
+      cairo_path_t *path;
+
+      path = cairo_copy_path (cr);
+      cairo_set_source_rgba (cr,
+                             border_color.red / 255.,
+                             border_color.green / 255.,
+                             border_color.blue / 255.,
+                             border_color.alpha / 255.);
+      cairo_fill (cr);
+
+      cairo_translate (cr, border_width, border_width);
+      cairo_scale (cr,
+                   (gdouble)(node->alloc_width - 2 * border_width) / node->alloc_width,
+                   (gdouble)(node->alloc_height - 2 * border_width) / node->alloc_height);
+      cairo_append_path (cr, path);
+      cairo_path_destroy (path);
+    }
+
+  cairo_set_source (cr, pattern);
+  cairo_fill (cr);
+
+  cairo_pattern_destroy (pattern);
+
+  texture = cogl_texture_new_from_data (node->alloc_width, node->alloc_height,
+                                        COGL_TEXTURE_NONE,
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                                        COGL_PIXEL_FORMAT_RGBA_8888_PRE,
+#elif G_BYTE_ORDER == G_BIG_ENDIAN
+                                        COGL_PIXEL_FORMAT_ARGB_8888_PRE,
+#else
+                                        COGL_PIXEL_FORMAT_ANY,
+#error unknown endianness type
+#endif
+                                        COGL_PIXEL_FORMAT_ANY,
+                                        rowstride,
+                                        data);
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+  g_free (data);
+
+  return texture;
+}
+
+void
+_st_theme_node_free_drawing_state (StThemeNode  *node)
+{
+  if (node->background_texture != COGL_INVALID_HANDLE)
+    cogl_handle_unref (node->background_texture);
+  if (node->shadow_material != COGL_INVALID_HANDLE)
+    cogl_handle_unref (node->shadow_material);
+  if (node->border_texture != COGL_INVALID_HANDLE)
+    cogl_handle_unref (node->border_texture);
+
+  _st_theme_node_init_drawing_state (node);
+}
+
+void
+_st_theme_node_init_drawing_state (StThemeNode *node)
+{
+  node->background_texture = COGL_INVALID_HANDLE;
+  node->shadow_material = COGL_INVALID_HANDLE;
+  node->border_texture = COGL_INVALID_HANDLE;
+}
+
+static void
+st_theme_node_render_resources (StThemeNode   *node,
+                                float          width,
+                                float          height)
+{
+  StTextureCache *texture_cache;
+  StBorderImage *border_image;
+  const char *background_image;
+
+  texture_cache = st_texture_cache_get_default ();
+
+  /* FIXME - need to separate this into things that need to be recomputed on
+   * geometry change versus things that can be cached regardless, such as
+   * a background image.
+   */
+  _st_theme_node_free_drawing_state (node);
+
+  node->alloc_width = width;
+  node->alloc_height = height;
+
+  _st_theme_node_ensure_background (node);
+  _st_theme_node_ensure_geometry (node);
+
+  /* Load referenced images from disk and draw anything we need with cairo now */
+
+  border_image = st_theme_node_get_border_image (node);
+  if (border_image)
+    {
+      const char *filename;
+
+      filename = st_border_image_get_filename (border_image);
+
+      node->border_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, filename);
+    }
+  else if (node->background_gradient_type != ST_GRADIENT_NONE)
+    {
+      node->border_texture = st_theme_node_render_gradient (node);
+    }
+
+  background_image = st_theme_node_get_background_image (node);
+  if (background_image != NULL)
+    {
+      StShadow *shadow_spec;
+
+      node->background_texture = st_texture_cache_load_file_to_cogl_texture (texture_cache, background_image);
+
+      shadow_spec = st_theme_node_get_shadow (node);
+      if (shadow_spec)
+        {
+          node->shadow_material = create_shadow_material (node, node->background_texture);
+        }
+    }
+
+  node->corner_texture = st_theme_node_lookup_corner (node, ST_CORNER_TOPLEFT);
+}
+
+static void
+paint_texture_with_opacity (CoglHandle       texture,
+                            ClutterActorBox *box,
+                            guint8           paint_opacity)
+{
+  if (paint_opacity == 255)
+    {
+      /* Minor: optimization use the default material if we can */
+      cogl_set_source_texture (texture);
+      cogl_rectangle (box->x1, box->y1, box->x2, box->y2);
+      return;
+    }
+
+  CoglHandle material;
+
+  material = cogl_material_new ();
+  cogl_material_set_layer (material, 0, texture);
+  cogl_material_set_color4ub (material,
+                              paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+
+  cogl_set_source (material);
+  cogl_rectangle (box->x1, box->y1, box->x2, box->y2);
+
+  cogl_handle_unref (material);
+}
+
+static void
+st_theme_node_paint_borders (StThemeNode           *node,
+                             const ClutterActorBox *box,
+                             guint8                 paint_opacity)
+
+{
+  float width, height;
+  int border_width;
+  int border_radius;
+  int max_width_radius;
+  ClutterColor border_color;
+  CoglHandle material;
+
+  width = box->x2 - box->x1;
+  height = box->y2 - box->y1;
+
+  get_arbitrary_border (node, &border_width, &border_color);
+  border_radius = node->border_radius[ST_CORNER_TOPLEFT];
+
+  max_width_radius = MAX(border_width, border_radius);
+
+  /* borders */
+  if (border_width > 0)
+    {
+      ClutterColor effective_border;
+
+      over (&border_color, &node->background_color, &effective_border);
+
+      cogl_set_source_color4ub (effective_border.red,
+                                effective_border.green,
+                                effective_border.blue,
+                                paint_opacity * effective_border.alpha / 255);
+
+      if (border_radius > 0) /* skip corners */
+        {
+          /* NORTH */
+          cogl_rectangle (max_width_radius, 0,
+                          width - max_width_radius, border_width);
+
+          /* EAST */
+          cogl_rectangle (width - border_width, max_width_radius,
+                          width, height - max_width_radius);
+
+          /* SOUTH */
+          cogl_rectangle (max_width_radius, height - border_width,
+                          width - max_width_radius, height);
+
+          /* WEST */
+          cogl_rectangle (0, max_width_radius,
+                          border_width, height - max_width_radius);
+        }
+      else /* include corners */
+        {
+          /* NORTH */
+          cogl_rectangle (0, 0,
+                          width, border_width);
+
+          /* EAST */
+          cogl_rectangle (width - border_width, border_width,
+                          width, height - border_width);
+
+          /* SOUTH */
+          cogl_rectangle (0, height - border_width,
+                          width, height);
+
+          /* WEST */
+          cogl_rectangle (0, border_width,
+                          border_width, height - border_width);
+        }
+    }
+
+  /* corners */
+  if (node->corner_texture != COGL_INVALID_HANDLE)
+    {
+      material = cogl_material_new ();
+      cogl_material_set_layer (material, 0, node->corner_texture);
+      cogl_material_set_color4ub (material,
+                                  paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+
+      cogl_set_source (material);
+
+      cogl_rectangle_with_texture_coords (0, 0, max_width_radius, max_width_radius, 0, 0, 0.5, 0.5);
+      cogl_rectangle_with_texture_coords (width - max_width_radius, 0, width, max_width_radius, 0.5, 0, 1, 0.5);
+      cogl_rectangle_with_texture_coords (width - max_width_radius, height - max_width_radius, width, height, 0.5, 0.5, 1, 1);
+      cogl_rectangle_with_texture_coords (0, height - max_width_radius, max_width_radius, height, 0, 0.5, 0.5, 1);
+
+      cogl_handle_unref (material);
+    }
+
+  /* background color */
+  cogl_set_source_color4ub (node->background_color.red,
+                            node->background_color.green,
+                            node->background_color.blue,
+                            paint_opacity * node->background_color.alpha / 255);
+
+  if (border_radius > border_width)
+    {
+      /* Once we've drawn the borders and corners, if the corners are bigger
+       * the the border width, the remaining area is shaped like
+       *
+       *  ########
+       * ##########
+       * ##########
+       *  ########
+       *
+       * We draw it in 3 pieces - first the top and bottom, then the main
+       * rectangle
+       */
+      cogl_rectangle (border_radius, border_width,
+                      width - border_radius, border_radius);
+      cogl_rectangle (border_radius, height - border_radius,
+                      width - border_radius, height - border_width);
+    }
+
+  cogl_rectangle (border_width, max_width_radius,
+                  width - border_width, height - max_width_radius);
+}
+
+static void
+st_theme_node_paint_sliced_border_image (StThemeNode           *node,
+                                         const ClutterActorBox *box,
+                                         guint8                 paint_opacity)
+{
+  gfloat ex, ey;
+  gfloat tx1, ty1, tx2, ty2;
+  gint border_left, border_right, border_top, border_bottom;
+  float img_width, img_height;
+  StBorderImage *border_image;
+  CoglHandle material;
+
+  border_image = st_theme_node_get_border_image (node);
+  g_assert (border_image != NULL);
+
+  st_border_image_get_borders (border_image,
+                               &border_left, &border_right, &border_top, &border_bottom);
+
+  img_width = cogl_texture_get_width (node->border_texture);
+  img_height = cogl_texture_get_height (node->border_texture);
+
+  tx1 = border_left / img_width;
+  tx2 = (img_width - border_right) / img_width;
+  ty1 = border_top / img_height;
+  ty2 = (img_height - border_bottom) / img_height;
+
+  ex = node->alloc_width - border_right;
+  if (ex < 0)
+    ex = border_right;           /* FIXME ? */
+
+  ey = node->alloc_height - border_bottom;
+  if (ey < 0)
+    ey = border_bottom;          /* FIXME ? */
+
+  material = cogl_material_new ();
+  cogl_material_set_layer (material, 0, node->border_texture);
+  cogl_material_set_color4ub (material,
+                              paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+
+  cogl_set_source (material);
+
+  {
+    GLfloat rectangles[] =
+    {
+      /* top left corner */
+      0, 0, border_left, border_top,
+      0.0, 0.0,
+      tx1, ty1,
+
+      /* top middle */
+      border_left, 0, ex, border_top,
+      tx1, 0.0,
+      tx2, ty1,
+
+      /* top right */
+      ex, 0, node->alloc_width, border_top,
+      tx2, 0.0,
+      1.0, ty1,
+
+      /* mid left */
+      0, border_top, border_left, ey,
+      0.0, ty1,
+      tx1, ty2,
+
+      /* center */
+      border_left, border_top, ex, ey,
+      tx1, ty1,
+      tx2, ty2,
+
+      /* mid right */
+      ex, border_top, node->alloc_width, ey,
+      tx2, ty1,
+      1.0, ty2,
+
+      /* bottom left */
+      0, ey, border_left, node->alloc_height,
+      0.0, ty2,
+      tx1, 1.0,
+
+      /* bottom center */
+      border_left, ey, ex, node->alloc_height,
+      tx1, ty2,
+      tx2, 1.0,
+
+      /* bottom right */
+      ex, ey, node->alloc_width, node->alloc_height,
+      tx2, ty2,
+      1.0, 1.0
+    };
+
+    cogl_rectangles_with_texture_coords (rectangles, 9);
+  }
+
+  cogl_handle_unref (material);
+}
+
+void
+st_theme_node_paint (StThemeNode           *node,
+                     const ClutterActorBox *box,
+                     guint8                 paint_opacity)
+{
+  float width, height;
+  ClutterActorBox allocation;
+
+  /* Some things take an ActorBox, some things just width/height */
+  width = box->x2 - box->x1;
+  height = box->y2 - box->y1;
+  allocation.x1 = allocation.y1 = 0;
+  allocation.x2 = width;
+  allocation.y2 = height;
+
+  if (node->alloc_width != width || node->alloc_height != height)
+    st_theme_node_render_resources (node, width, height);
+
+  /* Rough notes about the relationship of borders and backgrounds in CSS3;
+   * see http://www.w3.org/TR/css3-background/ for more accurate details.
+   *
+   * - Things are drawn in 4 layers, from the bottom:
+   *     Background color
+   *     Background image
+   *     Border color or border image
+   *     Content
+   * - The background color, gradient and image extend to and are clipped by
+   *   the edge of the border area, so will be rounded if the border is
+   *   rounded. (CSS3 background-clip property modifies this)
+   * - The border image replaces what would normally be drawn by the border
+   * - The border image is not clipped by a rounded border-radius
+   * - The border radius rounds the background even if the border is
+   *   zero width or a border image is being used.
+   *
+   * Deviations from the above as implemented here:
+   *  - Nonuniform border widths combined with a non-zero border radius result
+   *    in the border radius being ignored
+   *  - The combination of border image and a non-zero border radius is
+   *    not supported; the background color will be drawn with square
+   *    corners.
+   *  - The combination of border image and a background gradient is not
+   *    supported; the background will be drawn as a solid color
+   *  - The background image is drawn above the border color or image,
+   *    not below it.
+   *  - We don't clip the background image to the (rounded) border area.
+   *
+   * The first three allow us to always draw with no more than a single
+   * border_image and a single background image above it.
+   */
+
+  if (node->border_texture != COGL_INVALID_HANDLE)
+    {
+      /* Gradients and border images are mutually exclusive at this time */
+      if (node->background_gradient_type != ST_GRADIENT_NONE)
+        paint_texture_with_opacity (node->border_texture, &allocation, paint_opacity);
+      else
+        st_theme_node_paint_sliced_border_image (node, &allocation, paint_opacity);
+    }
+  else
+    st_theme_node_paint_borders (node, box, paint_opacity);
+
+  if (node->background_texture != COGL_INVALID_HANDLE)
+    {
+      ClutterActorBox background_box;
+
+      get_background_position (node, &allocation, &background_box);
+
+      /* CSS based drop shadows
+       *
+       * Drop shadows in ST are modelled after the CSS3 box-shadow property;
+       * see http://www.css3.info/preview/box-shadow/ for a detailed description.
+       *
+       * While the syntax of the property is mostly identical - we do not support
+       * multiple shadows and allow for a more liberal placement of the color
+       * parameter - its interpretation defers significantly in that the shadow's
+       * shape is not determined by the bounding box, but by the CSS background
+       * image (we could exend this in the future to take other CSS properties
+       * like boder and background color into account).
+       */
+      if (node->shadow_material != COGL_INVALID_HANDLE)
+        {
+          StShadow *shadow_spec;
+          ClutterActorBox shadow_box;
+
+          shadow_spec = node->shadow;
+
+          shadow_box.x1 = background_box.x1 + shadow_spec->xoffset - shadow_spec->blur;
+          shadow_box.y1 = background_box.y1 + shadow_spec->yoffset - shadow_spec->blur;
+          shadow_box.x2 = background_box.x2 + shadow_spec->xoffset + shadow_spec->blur;
+          shadow_box.y2 = background_box.y2 + shadow_spec->yoffset + shadow_spec->blur;
+
+          cogl_material_set_color4ub (node->shadow_material,
+                                      paint_opacity, paint_opacity, paint_opacity, paint_opacity);
+
+          cogl_set_source (node->shadow_material);
+          cogl_rectangle_with_texture_coords (shadow_box.x1, shadow_box.y1, shadow_box.x2, shadow_box.y2,
+                                              0, 0, 1, 1);
+        }
+
+      paint_texture_with_opacity (node->background_texture, &background_box, paint_opacity);
+    }
+}
diff --git a/src/st/st-theme-node-private.h b/src/st/st-theme-node-private.h
new file mode 100644 (file)
index 0000000..6074d5a
--- /dev/null
@@ -0,0 +1,86 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __ST_THEME_NODE_PRIVATE_H__
+#define __ST_THEME_NODE_PRIVATE_H__
+
+#include <gdk/gdk.h>
+
+#include "st-theme-node.h"
+
+G_BEGIN_DECLS
+
+struct _StThemeNode {
+  GObject parent;
+
+  StThemeContext *context;
+  StThemeNode *parent_node;
+  StTheme *theme;
+
+  PangoFontDescription *font_desc;
+
+  ClutterColor background_color;
+  /* If gradient is set, then background_color is the gradient start */
+  StGradientType background_gradient_type;
+  ClutterColor background_gradient_end;
+
+  ClutterColor foreground_color;
+  ClutterColor border_color[4];
+
+  int border_width[4];
+  int border_radius[4];
+  guint padding[4];
+
+  int width;
+  int height;
+  int min_width;
+  int min_height;
+  int max_width;
+  int max_height;
+
+  char *background_image;
+  StBorderImage *border_image;
+  StShadow *shadow;
+
+  GType element_type;
+  char *element_id;
+  char *element_class;
+  char *pseudo_class;
+  char *inline_style;
+
+  CRDeclaration **properties;
+  int n_properties;
+
+  /* We hold onto these separately so we can destroy them on finalize */
+  CRDeclaration *inline_properties;
+
+  guint properties_computed : 1;
+  guint geometry_computed : 1;
+  guint background_computed : 1;
+  guint foreground_computed : 1;
+  guint border_image_computed : 1;
+  guint shadow_computed : 1;
+  guint link_type : 2;
+
+  /* Graphics state */
+  float alloc_width;
+  float alloc_height;
+
+  CoglHandle shadow_material;
+  CoglHandle background_texture;
+  CoglHandle border_texture;
+  CoglHandle corner_texture;
+};
+
+struct _StThemeNodeClass {
+  GObjectClass parent_class;
+
+};
+
+void _st_theme_node_ensure_background (StThemeNode *node);
+void _st_theme_node_ensure_geometry (StThemeNode *node);
+
+void _st_theme_node_init_drawing_state (StThemeNode *node);
+void _st_theme_node_free_drawing_state (StThemeNode *node);
+
+G_END_DECLS
+
+#endif /* __ST_THEME_NODE_PRIVATE_H__ */
index 5492a947512d5554928d8fbbe9dbf2ca072b0b1d..1b8b4d0b2831ce2bfb02cf86543502e0a3c8cbac 100644 (file)
@@ -5,70 +5,12 @@
 
 #include "st-theme-private.h"
 #include "st-theme-context.h"
-#include "st-theme-node.h"
+#include "st-theme-node-private.h"
 
 static void st_theme_node_init               (StThemeNode          *node);
 static void st_theme_node_class_init         (StThemeNodeClass     *klass);
 static void st_theme_node_finalize           (GObject                 *object);
 
-struct _StThemeNode {
-  GObject parent;
-
-  StThemeContext *context;
-  StThemeNode *parent_node;
-  StTheme *theme;
-
-  PangoFontDescription *font_desc;
-
-  ClutterColor background_color;
-  /* If gradient is set, then background_color is the gradient start */
-  StGradientType background_gradient_type;
-  ClutterColor background_gradient_end;
-
-  ClutterColor foreground_color;
-  ClutterColor border_color[4];
-
-  int border_width[4];
-  int border_radius[4];
-  guint padding[4];
-
-  int width;
-  int height;
-  int min_width;
-  int min_height;
-  int max_width;
-  int max_height;
-
-  char *background_image;
-  StBorderImage *border_image;
-  StShadow *shadow;
-
-  GType element_type;
-  char *element_id;
-  char *element_class;
-  char *pseudo_class;
-  char *inline_style;
-
-  CRDeclaration **properties;
-  int n_properties;
-
-  /* We hold onto these separately so we can destroy them on finalize */
-  CRDeclaration *inline_properties;
-
-  guint properties_computed : 1;
-  guint geometry_computed : 1;
-  guint background_computed : 1;
-  guint foreground_computed : 1;
-  guint border_image_computed : 1;
-  guint shadow_computed : 1;
-  guint link_type : 2;
-};
-
-struct _StThemeNodeClass {
-  GObjectClass parent_class;
-
-};
-
 static const ClutterColor BLACK_COLOR = { 0, 0, 0, 0xff };
 static const ClutterColor TRANSPARENT_COLOR = { 0, 0, 0, 0 };
 
@@ -77,6 +19,7 @@ G_DEFINE_TYPE (StThemeNode, st_theme_node, G_TYPE_OBJECT)
 static void
 st_theme_node_init (StThemeNode *node)
 {
+  _st_theme_node_init_drawing_state (node);
 }
 
 static void
@@ -131,6 +74,8 @@ st_theme_node_finalize (GObject *object)
   if (node->background_image)
     g_free (node->background_image);
 
+  _st_theme_node_free_drawing_state (node);
+
   G_OBJECT_CLASS (st_theme_node_parent_class)->finalize (object);
 }
 
@@ -259,7 +204,7 @@ st_theme_node_get_pseudo_class (StThemeNode *node)
   return node->pseudo_class;
 }
 
-static void
+void
 ensure_properties (StThemeNode *node)
 {
   if (!node->properties_computed)
@@ -1108,8 +1053,8 @@ do_size_property (StThemeNode   *node,
   get_length_from_term_int (node, decl->value, FALSE, node_value);
 }
 
-static void
-ensure_geometry (StThemeNode *node)
+void
+_st_theme_node_ensure_geometry (StThemeNode *node)
 {
   int i, j;
 
@@ -1192,7 +1137,7 @@ st_theme_node_get_border_width (StThemeNode *node,
   g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
   g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   return node->border_width[side];
 }
@@ -1204,7 +1149,7 @@ st_theme_node_get_border_radius (StThemeNode *node,
   g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
   g_return_val_if_fail (corner >= ST_CORNER_TOPLEFT && corner <= ST_CORNER_BOTTOMLEFT, 0.);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   return node->border_radius[corner];
 }
@@ -1214,7 +1159,7 @@ st_theme_node_get_width (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->width;
 }
 
@@ -1223,7 +1168,7 @@ st_theme_node_get_height (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->height;
 }
 
@@ -1232,7 +1177,7 @@ st_theme_node_get_min_width (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->min_width;
 }
 
@@ -1241,7 +1186,7 @@ st_theme_node_get_min_height (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->min_height;
 }
 
@@ -1250,7 +1195,7 @@ st_theme_node_get_max_width (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->max_width;
 }
 
@@ -1259,7 +1204,7 @@ st_theme_node_get_max_height (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), -1);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
   return node->max_height;
 }
 
@@ -1281,8 +1226,8 @@ get_background_color_from_term (StThemeNode  *node,
   return result;
 }
 
-static void
-ensure_background (StThemeNode *node)
+void
+_st_theme_node_ensure_background (StThemeNode *node)
 {
   int i;
 
@@ -1429,7 +1374,7 @@ st_theme_node_get_background_color (StThemeNode  *node,
 {
   g_return_if_fail (ST_IS_THEME_NODE (node));
 
-  ensure_background (node);
+  _st_theme_node_ensure_background (node);
 
   *color = node->background_color;
 }
@@ -1439,7 +1384,7 @@ st_theme_node_get_background_image (StThemeNode *node)
 {
   g_return_val_if_fail (ST_IS_THEME_NODE (node), NULL);
 
-  ensure_background (node);
+  _st_theme_node_ensure_background (node);
 
   return node->background_image;
 }
@@ -1500,7 +1445,7 @@ st_theme_node_get_background_gradient (StThemeNode    *node,
 {
   g_return_if_fail (ST_IS_THEME_NODE (node));
 
-  ensure_background (node);
+  _st_theme_node_ensure_background (node);
 
   *type = node->background_gradient_type;
   if (*type != ST_GRADIENT_NONE)
@@ -1518,7 +1463,7 @@ st_theme_node_get_border_color (StThemeNode  *node,
   g_return_if_fail (ST_IS_THEME_NODE (node));
   g_return_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   *color = node->border_color[side];
 }
@@ -1530,7 +1475,7 @@ st_theme_node_get_padding (StThemeNode *node,
   g_return_val_if_fail (ST_IS_THEME_NODE (node), 0.);
   g_return_val_if_fail (side >= ST_SIDE_TOP && side <= ST_SIDE_LEFT, 0.);
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   return node->padding[side];
 }
@@ -2353,7 +2298,7 @@ st_theme_node_adjust_preferred_width (StThemeNode  *node,
 
   g_return_if_fail (ST_IS_THEME_NODE (node));
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   width_inc = get_width_inc (node);
 
@@ -2420,7 +2365,7 @@ st_theme_node_adjust_preferred_height (StThemeNode  *node,
 
   g_return_if_fail (ST_IS_THEME_NODE (node));
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   height_inc = get_height_inc (node);
 
@@ -2461,7 +2406,7 @@ st_theme_node_get_content_box (StThemeNode           *node,
 
   g_return_if_fail (ST_IS_THEME_NODE (node));
 
-  ensure_geometry (node);
+  _st_theme_node_ensure_geometry (node);
 
   avail_width = allocation->x2 - allocation->x1;
   avail_height = allocation->y2 - allocation->y1;
@@ -2501,8 +2446,8 @@ st_theme_node_geometry_equal (StThemeNode *node,
 {
   StSide side;
 
-  ensure_geometry (node);
-  ensure_geometry (other);
+  _st_theme_node_ensure_geometry (node);
+  _st_theme_node_ensure_geometry (other);
 
   for (side = ST_SIDE_TOP; side <= ST_SIDE_LEFT; side++)
     {
index 87c6884eea99081a5e5ea2bf460c8c004ad1719e..2bc5e7ab449d06ebc43d2a077fe544061fc46f93 100644 (file)
@@ -171,6 +171,11 @@ void st_theme_node_get_content_box         (StThemeNode        *node,
 gboolean st_theme_node_geometry_equal (StThemeNode *node,
                                        StThemeNode *other);
 
+void st_theme_node_paint (StThemeNode            *node,
+                          const ClutterActorBox  *box,
+                          guint8                  paint_opacity);
+
+
 G_END_DECLS
 
 #endif /* __ST_THEME_NODE_H__ */
index 92e2d9a43d4570cb33f69e921cbc5e36933d48e0..dba5a040eb72e3723deb4d2a0a156fcdba1dd718 100644 (file)
@@ -38,9 +38,7 @@
 
 #include "st-marshal.h"
 #include "st-private.h"
-#include "st-shadow-texture.h"
 #include "st-texture-cache.h"
-#include "st-texture-frame.h"
 #include "st-theme-context.h"
 #include "st-tooltip.h"
 
@@ -59,7 +57,6 @@ struct _StWidgetPrivate
 
   ClutterActor *border_image;
   ClutterActor *background_image;
-  ClutterActor *background_image_shadow;
   ClutterColor  bg_color;
 
   guint         border_width;
@@ -68,6 +65,7 @@ struct _StWidgetPrivate
   StGradientType bg_gradient_type;
   ClutterColor  bg_gradient_end;
 
+  ClutterActorBox background_allocation;
   gdouble       shadow_xoffset;
   gdouble       shadow_yoffset;
 
@@ -125,7 +123,6 @@ G_DEFINE_ABSTRACT_TYPE (StWidget, st_widget, CLUTTER_TYPE_ACTOR);
 
 static void st_widget_recompute_style (StWidget    *widget,
                                        StThemeNode *old_theme_node);
-static void st_widget_redraw_gradient (StWidget  *widget);
 
 static void
 st_widget_set_property (GObject      *gobject,
@@ -254,12 +251,6 @@ st_widget_dispose (GObject *gobject)
       priv->border_image = NULL;
     }
 
-  if (priv->background_image_shadow)
-    {
-      clutter_actor_unparent (priv->background_image_shadow);
-      priv->background_image_shadow = NULL;
-    }
-
   if (priv->theme_node)
     {
       g_object_unref (priv->theme_node);
@@ -295,18 +286,20 @@ st_widget_finalize (GObject *gobject)
   G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
 }
 
+
 static void
 st_widget_allocate (ClutterActor          *actor,
                     const ClutterActorBox *box,
                     ClutterAllocationFlags flags)
 {
-  StWidgetPrivate *priv = ST_WIDGET (actor)->priv;
+  StWidget *self = ST_WIDGET (actor);
+  StWidgetPrivate *priv = self->priv;
   StThemeNode *theme_node;
   ClutterActorClass *klass;
   ClutterGeometry area;
   ClutterVertex in_v, out_v;
 
-  theme_node = st_widget_get_theme_node ((StWidget*) actor);
+  theme_node = st_widget_get_theme_node (self);
 
   klass = CLUTTER_ACTOR_CLASS (st_widget_parent_class);
   klass->allocate (actor, box, flags);
@@ -327,249 +320,20 @@ st_widget_allocate (ClutterActor          *actor,
 
       st_tooltip_set_tip_area (priv->tooltip, &area);
     }
-
-
-
-  if (priv->border_image && priv->bg_gradient_type == ST_GRADIENT_NONE)
-    {
-      ClutterActorBox frame_box;
-
-      frame_box.x1 = frame_box.y1 = 0;
-      frame_box.x2 = box->x2 - box->x1;
-      frame_box.y2 = box->y2 - box->y1;
-
-      clutter_actor_allocate (CLUTTER_ACTOR (priv->border_image),
-                              &frame_box,
-                              flags);
-    }
-  else if (priv->bg_gradient_type != ST_GRADIENT_NONE)
-    {
-      guint width,  old_width,
-            height, old_height;
-      ClutterActorBox frame_box;
-
-      frame_box.x1 = frame_box.y1 = 0;
-      frame_box.x2 = box->x2 - box->x1;
-      frame_box.y2 = box->y2 - box->y1;
-
-      width = (guint)(0.5 + frame_box.x2);
-      height = (guint)(0.5 + frame_box.y2);
-
-      clutter_cairo_texture_get_surface_size (CLUTTER_CAIRO_TEXTURE (priv->border_image),
-                                              &old_width, &old_height);
-
-      if (width > 0 && height > 0 &&
-          (old_width != width || old_height != height))
-        {
-
-          clutter_cairo_texture_set_surface_size (CLUTTER_CAIRO_TEXTURE (priv->border_image),
-                                                  width, height);
-          st_widget_redraw_gradient ((StWidget*) actor);
-        }
-      clutter_actor_allocate (CLUTTER_ACTOR (priv->border_image),
-                              &frame_box,
-                              flags);
-    }
-
-  if (priv->background_image)
-    {
-      ClutterActorBox frame_box;
-      gfloat w, h;
-
-      frame_box.x1 = frame_box.y1 = 0;
-      frame_box.x2 = box->x2 - box->x1;
-      frame_box.y2 = box->y2 - box->y1;
-
-      clutter_actor_get_size (CLUTTER_ACTOR (priv->background_image), &w, &h);
-
-      /* scale the background into the allocated bounds */
-      if (w > frame_box.x2 || h > frame_box.y2)
-        {
-          gint new_h, new_w, offset;
-          gint box_w, box_h;
-
-          box_w = (int) frame_box.x2;
-          box_h = (int) frame_box.y2;
-
-          /* scale to fit */
-          new_h = (int)((h / w) * ((gfloat) box_w));
-          new_w = (int)((w / h) * ((gfloat) box_h));
-
-          if (new_h > box_h)
-            {
-              /* center for new width */
-              offset = ((box_w) - new_w) * 0.5;
-              frame_box.x1 = offset;
-              frame_box.x2 = offset + new_w;
-
-              frame_box.y2 = box_h;
-            }
-          else
-            {
-              /* center for new height */
-              offset = ((box_h) - new_h) * 0.5;
-              frame_box.y1 = offset;
-              frame_box.y2 = offset + new_h;
-
-              frame_box.x2 = box_w;
-            }
-
-        }
-      else
-        {
-          /* center the background on the widget */
-          frame_box.x1 = (int)(((box->x2 - box->x1) / 2) - (w / 2));
-          frame_box.y1 = (int)(((box->y2 - box->y1) / 2) - (h / 2));
-          frame_box.x2 = frame_box.x1 + w;
-          frame_box.y2 = frame_box.y1 + h;
-        }
-
-        if (priv->background_image_shadow)
-          {
-            StShadowTexture *shadow;
-            ClutterActorBox  shadow_box;
-
-            shadow_box.x1 = frame_box.x1 + priv->shadow_xoffset;
-            shadow_box.y1 = frame_box.y1 + priv->shadow_yoffset;
-            shadow_box.x2 = frame_box.x2 + priv->shadow_xoffset;
-            shadow_box.y2 = frame_box.y2 + priv->shadow_yoffset;
-
-            /* The shadow texture is larger than the original image due
-               to blurring, so we let it adjust its size.
-               When the original image has been scaled, this will change
-               the effective blur radius - we ignore this for now. */
-            shadow = ST_SHADOW_TEXTURE (priv->background_image_shadow);
-            st_shadow_texture_adjust_allocation (shadow, &shadow_box);
-
-            clutter_actor_allocate (priv->background_image_shadow,
-                                    &shadow_box, flags);
-          }
-
-
-      clutter_actor_allocate (CLUTTER_ACTOR (priv->background_image),
-                              &frame_box,
-                              flags);
-    }
 }
 
 static void
-st_widget_real_draw_background (StWidget *self)
+st_widget_paint (ClutterActor *actor)
 {
-  StWidgetPrivate *priv = self->priv;
-  ClutterActor *actor = CLUTTER_ACTOR (self);
-  ClutterActorBox allocation = { 0, };
-  gfloat w, h;
-  guint8 opacity;
-
-  clutter_actor_get_allocation_box (actor, &allocation);
-  w = allocation.x2 - allocation.x1;
-  h = allocation.y2 - allocation.y1;
-
-  opacity = clutter_actor_get_paint_opacity (actor);
-
-  /* Default implementation just draws the background
-   * colour and the image on top
-   */
-  if (priv->draw_bg_color)
-    {
-      ClutterColor bg_color = priv->bg_color;
-
-      bg_color.alpha = opacity * bg_color.alpha / 255;
-
-      cogl_set_source_color4ub (bg_color.red,
-                                bg_color.green,
-                                bg_color.blue,
-                                bg_color.alpha);
-      cogl_rectangle (0, 0, w, h);
-    }
-
-  if (priv->draw_border_internal)
-    {
-      StThemeNode *node = st_widget_get_theme_node (self);
-      int side;
-      double border_top, border_right, border_bottom, border_left;
-
-      border_top = st_theme_node_get_border_width (node, ST_SIDE_TOP);
-      border_right = st_theme_node_get_border_width (node, ST_SIDE_RIGHT);
-      border_bottom = st_theme_node_get_border_width (node, ST_SIDE_BOTTOM);
-      border_left = st_theme_node_get_border_width (node, ST_SIDE_LEFT);
-
-      for (side = 0; side < 4; side++)
-        {
-          ClutterColor color;
-
-          switch (side)
-          {
-            case ST_SIDE_TOP:
-              if (border_top <= 0)
-                continue;
-              break;
-            case ST_SIDE_RIGHT:
-              if (border_right <= 0)
-                continue;
-              break;
-            case ST_SIDE_BOTTOM:
-              if (border_bottom <= 0)
-                continue;
-              break;
-            case ST_SIDE_LEFT:
-              if (border_left <= 0)
-                continue;
-              break;
-          }
-
-          st_theme_node_get_border_color (node, side, &color);
-
-          color.alpha = (color.alpha * opacity) / 0xff;
-
-          cogl_set_source_color4ub (color.red,
-                                    color.green,
-                                    color.blue,
-                                    color.alpha);
-
-          /* Note top and bottom extend to the ends, left/right
-           * are constrained by them.  See comment above about CSS
-           * conformance.
-           */
-          switch (side)
-          {
-            case ST_SIDE_TOP:
-              cogl_rectangle (0, 0,
-                              w, border_top);
-              break;
-            case ST_SIDE_RIGHT:
-              cogl_rectangle (w - border_right, border_top,
-                              w, h - border_bottom);
-              break;
-            case ST_SIDE_BOTTOM:
-              cogl_rectangle (0, h - border_bottom,
-                              w, h);
-              break;
-            case ST_SIDE_LEFT:
-              cogl_rectangle (0, border_top,
-                              border_left, h - border_bottom);
-              break;
-            }
-        }
-    }
-
-  if (priv->border_image)
-    clutter_actor_paint (priv->border_image);
-}
+  StWidget *self = ST_WIDGET (actor);
+  StThemeNode *theme_node;
+  ClutterActorBox allocation;
 
-static void
-st_widget_paint (ClutterActor *self)
-{
-  StWidgetPrivate *priv = ST_WIDGET (self)->priv;
+  theme_node = st_widget_get_theme_node (self);
 
-  st_widget_real_draw_background (ST_WIDGET (self));
+  clutter_actor_get_allocation_box (actor, &allocation);
 
-  if (priv->background_image != NULL)
-    {
-      if (priv->background_image_shadow)
-        clutter_actor_paint (priv->background_image_shadow);
-      clutter_actor_paint (priv->background_image);
-    }
+  st_theme_node_paint (theme_node, &allocation, clutter_actor_get_paint_opacity (actor));
 }
 
 static void
@@ -599,9 +363,6 @@ st_widget_map (ClutterActor *actor)
 
   st_widget_ensure_style ((StWidget*) actor);
 
-  if (priv->background_image_shadow)
-    clutter_actor_map (priv->background_image_shadow);
-
   if (priv->border_image)
     clutter_actor_map (priv->border_image);
 
@@ -619,9 +380,6 @@ st_widget_unmap (ClutterActor *actor)
 
   CLUTTER_ACTOR_CLASS (st_widget_parent_class)->unmap (actor);
 
-  if (priv->background_image_shadow)
-    clutter_actor_unmap (priv->background_image_shadow);
-
   if (priv->border_image)
     clutter_actor_unmap (priv->border_image);
 
@@ -632,125 +390,6 @@ st_widget_unmap (ClutterActor *actor)
     clutter_actor_unmap ((ClutterActor *) priv->tooltip);
 }
 
-static void
-st_widget_redraw_gradient (StWidget  *widget)
-{
-  ClutterCairoTexture *texture;
-  ClutterColor *start, *end;
-  StWidgetPrivate *priv;
-  guint width, height;
-  guint radius[4], i;
-  cairo_t *cr;
-  cairo_pattern_t *pattern;
-  gboolean round_border = FALSE;
-
-  if (widget->priv->bg_gradient_type == ST_GRADIENT_NONE)
-    return;
-
-  texture = CLUTTER_CAIRO_TEXTURE (widget->priv->border_image);
-  priv  = widget->priv;
-  start = &widget->priv->bg_color;
-  end   = &widget->priv->bg_gradient_end;
-
-  for (i = 0; i < 4; i++)
-    {
-      radius[i] = st_theme_node_get_border_radius (priv->theme_node, i);
-      if (radius[i] > 0)
-        round_border = TRUE;
-    }
-
-  clutter_cairo_texture_get_surface_size (texture, &width, &height);
-  clutter_cairo_texture_clear (texture);
-  cr = clutter_cairo_texture_create (texture);
-
-  if (priv->bg_gradient_type == ST_GRADIENT_VERTICAL)
-    pattern = cairo_pattern_create_linear (0, 0, 0, height);
-  else if (priv->bg_gradient_type == ST_GRADIENT_HORIZONTAL)
-    pattern = cairo_pattern_create_linear (0, 0, width, 0);
-  else
-    {
-      gdouble cx, cy;
-
-      cx = width / 2.;
-      cy = height / 2.;
-      pattern = cairo_pattern_create_radial (cx, cy, 0, cx, cy, MIN (cx, cy));
-    }
-
-  cairo_pattern_add_color_stop_rgba (pattern, 0,
-                                     start->red / 255.,
-                                     start->green / 255.,
-                                     start->blue / 255.,
-                                     start->alpha / 255.);
-  cairo_pattern_add_color_stop_rgba (pattern, 1,
-                                     end->red / 255.,
-                                     end->green / 255.,
-                                     end->blue / 255.,
-                                     end->alpha / 255.);
-
-  if (round_border)
-    {
-      if (radius[ST_CORNER_TOPLEFT] > 0)
-        cairo_arc (cr,
-                   radius[ST_CORNER_TOPLEFT],
-                   radius[ST_CORNER_TOPLEFT],
-                   radius[ST_CORNER_TOPLEFT], M_PI, 3 * M_PI / 2);
-      else
-        cairo_move_to (cr, 0, 0);
-      cairo_line_to (cr, width - radius[ST_CORNER_TOPRIGHT], 0);
-      if (radius[ST_CORNER_TOPRIGHT] > 0)
-        cairo_arc (cr,
-                   width - radius[ST_CORNER_TOPRIGHT],
-                   radius[ST_CORNER_TOPRIGHT],
-                   radius[ST_CORNER_TOPRIGHT], 3 * M_PI / 2, 2 * M_PI);
-      cairo_line_to (cr, width, height - radius[ST_CORNER_BOTTOMRIGHT]);
-      if (radius[ST_CORNER_BOTTOMRIGHT])
-        cairo_arc (cr,
-                   width - radius[ST_CORNER_BOTTOMRIGHT],
-                   height - radius[ST_CORNER_BOTTOMRIGHT],
-                   radius[ST_CORNER_BOTTOMRIGHT], 0, M_PI / 2);
-      cairo_line_to (cr, radius[ST_CORNER_BOTTOMLEFT], height);
-      if (radius[ST_CORNER_BOTTOMLEFT])
-        cairo_arc (cr,
-                   radius[ST_CORNER_BOTTOMLEFT],
-                   height - radius[ST_CORNER_BOTTOMLEFT],
-                   radius[ST_CORNER_BOTTOMLEFT], M_PI / 2, M_PI);
-      cairo_close_path (cr);
-    }
-  else
-    cairo_rectangle (cr, 0, 0, width, height);
-
-  if (priv->border_width > 0)
-    {
-      guint8 opacity;
-      gdouble effective_alpha;
-      cairo_path_t *path;
-
-      path = cairo_copy_path (cr);
-      opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (widget));
-      effective_alpha = priv->border_color.alpha * opacity / (255. * 255.);
-
-      cairo_set_source_rgba (cr,
-                             priv->border_color.red / 255.,
-                             priv->border_color.green / 255.,
-                             priv->border_color.blue / 255.,
-                             effective_alpha);
-      cairo_fill (cr);
-
-      cairo_translate (cr, priv->border_width, priv->border_width);
-      cairo_scale (cr,
-                   (gdouble)(width - 2 * priv->border_width) / width,
-                   (gdouble)(height - 2 * priv->border_width) / height);
-      cairo_append_path (cr, path);
-      cairo_path_destroy (path);
-    }
-
-  cairo_set_source (cr, pattern);
-  cairo_fill (cr);
-
-  cairo_pattern_destroy (pattern);
-  cairo_destroy (cr);
-}
-
 static void notify_children_of_style_change (ClutterContainer *container);
 
 static void
@@ -776,270 +415,12 @@ static void
 st_widget_real_style_changed (StWidget *self)
 {
   StWidgetPrivate *priv = ST_WIDGET (self)->priv;
-  StThemeNode *theme_node;
-  StBorderImage *border_image;
-  StShadow *shadow;
-  StTextureCache *texture_cache;
-  ClutterTexture *texture;
-  const char *bg_file = NULL;
-  gboolean relayout_needed = FALSE;
-  gboolean has_changed = FALSE;
-  ClutterColor color;
-  guint border_radius = 0;
-  StGradientType gradient;
-  ClutterColor gradient_end;
-  StSide side;
-  StCorner corner;
-  gboolean uniform_border_width;
 
   /* application has request this widget is not stylable */
   if (!priv->is_stylable)
     return;
 
-  theme_node = st_widget_get_theme_node (self);
-
-  st_theme_node_get_background_gradient (theme_node, &gradient, &color, &gradient_end);
-
-  if (gradient == ST_GRADIENT_NONE)
-    {
-      st_theme_node_get_background_color (theme_node, &color);
-      if (gradient != priv->bg_gradient_type ||
-          !clutter_color_equal (&color, &priv->bg_color))
-        {
-          priv->bg_gradient_type = gradient;
-          priv->bg_color = color;
-          priv->draw_bg_color = color.alpha != 0;
-          has_changed = TRUE;
-        }
-    }
-  else if (gradient != priv->bg_gradient_type ||
-           !clutter_color_equal (&color, &priv->bg_color) ||
-           !clutter_color_equal (&gradient_end, &priv->bg_gradient_end))
-    {
-      priv->bg_gradient_type = gradient;
-      priv->bg_color = color;
-      priv->bg_gradient_end = gradient_end;
-      priv->draw_bg_color = TRUE;
-      has_changed = TRUE;
-    }
-
-  if (priv->background_image_shadow)
-    {
-      clutter_actor_unparent (priv->background_image_shadow);
-      priv->background_image_shadow = NULL;
-    }
-
-  if (priv->border_image)
-    {
-      clutter_actor_unparent (priv->border_image);
-      priv->border_image = NULL;
-    }
-
-  if (priv->background_image)
-    {
-      clutter_actor_unparent (priv->background_image);
-      priv->background_image = NULL;
-    }
-
-  texture_cache = st_texture_cache_get_default ();
-
-
-  /* Rough notes about the relationship of borders and backgrounds in CSS3;
-   * see http://www.w3.org/TR/css3-background/ for more accurate details.
-   *
-   * - Things are drawn in 4 layers, from the bottom:
-   *     Background color
-   *     Background image
-   *     Border color or border image
-   *     Content
-   * - The background color, gradient and image extend to and are clipped by
-   *   the edge of the border area, so will be rounded if the border is
-   *   rounded. (CSS3 background-clip property modifies this)
-   * - The border image replaces what would normally be drawn by the border
-   * - The border image is not clipped by a rounded border-radius
-   * - The border radius rounds the background even if the border is
-   *   zero width or a border image is being used.
-   *
-   * Deviations from the above as implemented here:
-   *  - Nonuniform border widths combined with a non-zero border radius result
-   *    in the border radius being ignored
-   *  - The combination of border image and a non-zero border radius is
-   *    not supported; the background color will be drawn with square
-   *    corners.
-   *  - The combination of border image and a background gradient is not
-   *    supported; the background will be drawn as a solid color
-   *  - The background image is drawn above the border color or image,
-   *    not below it.
-   *  - We don't clip the background image to the (rounded) border area.
-   *
-   * The first three allow us to always draw with no more than a single
-   * border_image and a single background image above it.
-   */
-
-  /* Check whether all border widths are the same.  Also, acquire the
-   * first nonzero border width as well as the border color.
-   */
-  uniform_border_width = TRUE;
-  priv->border_width = st_theme_node_get_border_width (theme_node,
-                                                       ST_SIDE_TOP);
-  if (priv->border_width > 0.5)
-    priv->border_width = (int)(0.5 + priv->border_width);
-  for (side = 0; side < 4; side++)
-    {
-      double width = st_theme_node_get_border_width (theme_node, side);
-      if (width > 0.5)
-        width = (int)(0.5 + width);
-      if (width > 0)
-        {
-          priv->border_width = width;
-          st_theme_node_get_border_color (theme_node,
-                                          side, &priv->border_color);
-        }
-      if ((int)width != priv->border_width)
-        {
-          uniform_border_width = FALSE;
-          break;
-        }
-    }
-
-  /* Pick the first nonzero border radius, but only if we have a uniform border. */
-  if (uniform_border_width)
-    {
-      for (corner = 0; corner < 4; corner++)
-        {
-          double radius = st_theme_node_get_border_radius (theme_node, corner);
-          if (radius > 0.5)
-            {
-              border_radius = (int)(0.5 + radius);
-              break;
-            }
-        }
-    }
-
-  border_image = st_theme_node_get_border_image (theme_node);
-  if (border_image)
-    {
-      const char *filename;
-      gint border_left, border_right, border_top, border_bottom;
-      gint width, height;
-
-      filename = st_border_image_get_filename (border_image);
-
-      /* `border-image' takes precedence over `background-image'.
-       * Firefox lets the background-image shine thru when border-image has
-       * alpha an channel, maybe that would be an option for the future. */
-      texture = (ClutterTexture*) st_texture_cache_load_file_simple (texture_cache,
-                                                                     filename);
-
-      clutter_texture_get_base_size (CLUTTER_TEXTURE (texture),
-                                     &width, &height);
-
-      st_border_image_get_borders (border_image,
-                                   &border_left, &border_right, &border_top, &border_bottom);
-
-      priv->border_image = st_texture_frame_new (texture,
-                                                 border_top,
-                                                 border_right,
-                                                 border_bottom,
-                                                 border_left);
-      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
-
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-  else if (priv->bg_gradient_type != ST_GRADIENT_NONE)
-    {
-      priv->draw_border_internal = FALSE;
-      priv->draw_bg_color = FALSE;
-      texture = g_object_new (CLUTTER_TYPE_CAIRO_TEXTURE, NULL);
-      priv->border_image = CLUTTER_ACTOR (texture);
-      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
-
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-  else if (border_radius > 0)
-    {
-      priv->draw_border_internal = FALSE;
-      priv->draw_bg_color = FALSE;
-      priv->border_image = g_object_new (BIG_TYPE_RECTANGLE,
-                                        "color", &priv->bg_color,
-                                        "border-width", priv->border_width,
-                                        "border-color", &priv->border_color,
-                                        "corner-radius", border_radius,
-                                        NULL);
-
-      clutter_actor_set_parent (priv->border_image, CLUTTER_ACTOR (self));
-
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-  else if (priv->border_width > 0 && priv->border_color.alpha != 0)
-    {
-      priv->draw_bg_color = TRUE;
-      priv->draw_border_internal = TRUE;
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-  else if (priv->draw_border_internal)
-    {
-      priv->draw_border_internal = FALSE;
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-
-  bg_file = st_theme_node_get_background_image (theme_node);
-  if (bg_file != NULL)
-    {
-      priv->background_image = st_texture_cache_load_file_simple (texture_cache, bg_file);
-      clutter_actor_set_parent (priv->background_image, CLUTTER_ACTOR (self));
-
-      has_changed = TRUE;
-      relayout_needed = TRUE;
-    }
-
-  /* CSS based drop shadows
-   *
-   * Drop shadows in ST are modelled after the CSS3 box-shadow property;
-   * see http://www.css3.info/preview/box-shadow/ for a detailed description.
-   *
-   * While the syntax of the property is mostly identical - we do not support
-   * multiple shadows and allow for a more liberal placement of the color
-   * parameter - its interpretation defers significantly in that the shadow's
-   * shape is not determined by the bounding box, but by the CSS background
-   * image (we could exend this in the future to take other CSS properties
-   * like boder and background color into account).
-   */
-  shadow = st_theme_node_get_shadow (theme_node);
-  if (shadow != NULL)
-    {
-      priv->shadow_xoffset = shadow->xoffset;
-      priv->shadow_yoffset = shadow->yoffset;
-
-      if (priv->background_image)
-        {
-          priv->background_image_shadow =
-              st_shadow_texture_new (priv->background_image,
-                                     &shadow->color,
-                                     shadow->blur);
-
-          clutter_actor_set_parent (priv->background_image_shadow,
-                                    CLUTTER_ACTOR (self));
-          has_changed = TRUE;
-          relayout_needed = TRUE;
-        }
-    }
-
-  /* If there are any properties above that need to cause a relayout they
-   * should set this flag.
-   */
-  if (has_changed)
-    {
-      if (relayout_needed)
-        clutter_actor_queue_relayout ((ClutterActor *) self);
-      else
-        clutter_actor_queue_redraw ((ClutterActor *) self);
-    }
+  clutter_actor_queue_redraw ((ClutterActor *) self);
 
   if (CLUTTER_IS_CONTAINER (self))
     notify_children_of_style_change ((ClutterContainer *)self);